服务间通信之Http框架


  • 1.服务间通讯调用

  • 2.jersey代理连接池

  • 3.综上

1.服务间通信调用


首先不提在微服务中,就是在我们使用spring cloud技术栈构建我们的服务中,如果我们需要调用其他的服务或者第三方的服务,一般的通信方式无非是http通信、rpc通信、异步消息通信等等,当然大多数服务一般都是以http接口的形式提供出来,那么可以用来调用该服务的方法可谓是多种多样,各式各样的http客户端,apache Httpclient、OKHttp、jersey-client、原生HttpURLConnection等等,大体以上几种我都用过,做了一些对比,可以看一下:

1.1 java原生HttpURLConnection


这个用的不多,在正式项目中几乎没有用过,写一些小的demo的时候偶尔用过,使用的原因更多是当时懒得再引入其他第三方的http框架了,用法如下:

public static void sendPostJson() throws IOException {
        String path = "http://127.0.0.1:8080";
        JSONObject obj = new JSONObject();
        obj.put("id", "10001");
        obj.put("name", "xiaxuan");
        obj.put("sex", "M");
        String jsonStr = obj.toJSONString();
        byte[] data = jsonStr.getBytes();
        java.net.URL url = new java.net.URL(path);
        java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Connection", "keep-alive");
        conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        conn.setRequestProperty("Content-Length", String.valueOf(data.length));
        OutputStream outStream = conn.getOutputStream();
        outStream.write(data);
        outStream.flush();
        outStream.close();
        if(conn.getResponseCode() == 200){
            BufferedReader in = new BufferedReader(new InputStreamReader((InputStream) conn.getInputStream(), "UTF-8"));
            String msg = in.readLine();
            System.out.println("msg: " + msg);
            in.close();
        }
        conn.disconnect();
    }

不多说,用法还是挺复杂的,简单的请求甚至需要数十行才能完成,并且不易于理解,当时写完调试了半天才通,挺后悔用原生的HttpURLConnection来进行当时功能的测试,感觉比我当时写的一个http-server还要麻烦,在正式的项目中一般不会用到原生http请求类。

1.2 apache HttpClient


apache common下的子项目,高效、功能丰富、易于使用,这个在我所在的公司中的所负责的项目中用的不多,找了一个,用法如下:

public CloseableHttpResponse stream(String url) throws IOException {
        String fileId = url.substring(url.lastIndexOf("/"), url.length());
        url = this.bootConfig.getHttpFilesUri() + fileId;
        log.info("DefaultTransport files url: " + url);
        HttpGet get = new HttpGet(url);
        CloseableHttpResponse response = (CloseableHttpResponse)this.client.execute(get);
        return response;
    }

这个是内部请求一个下载图片使用的,get请求获取文件,使用比较简单,更多的使用方法还是请去官网看下吧:

http://hc.apache.org/httpclient-3.x/tutorial.html

1.3 OKhttp


这个是我在open server项目中使用的一款http框架,特性如下:

  • 支持http2.0,对一台机器的请求共享一个socket。

  • 采用连接池技术,可以有效的减少Http链接数量。

  • 无缝集成GZIP压缩技术。

  • 支持Response Cache,避免重复请求

  • 域名多IP支持。

以下是我在以前写的一个简单demo,项目中使用的是okhttp连接池,设置的参数较多,各种拦截器之类的,使用比较复杂,不再展示出来。

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}

给我的感觉是确实是好用,相当易用的api,设置读取超时、写入超时、重试拦截器、长连接等等都很容易实现,在先前的文章
Spring boot使用ProxyFilter进行服务代理,这篇文章中使用的ProxyFilter里面原生使用的http连接池是使用的HttpClient的连接池,但是连接池中连接数太少,各种参数的配置不适合于我们系统的开发,替换成了OkHttp的连接池,后来我升级为Okhttp3之后,加上的重试拦截器等等。另外OkHttp的特性中还提到了另外一点,就是OkHttp还处理了代理服务器问题和SSL握手失败问题,ssl握手失败问题是比较难定位和追踪的,在将公司内我所负责的模块升级为java8以后,因为https产生的问题就是多个,带来不少的麻烦。

1.4 jersey client


我所负责的另外一个项目中,使用的rest框架是jersey,调用其他服务使用的是jersey提供的jersey-client,用法如下:

    private static Client getClient () {
        return ClientBuilder.newClient().register(JacksonFeature.class);
    }

    public static EnterpriseRs openDomain (String url, EnterpriseDomain domain) {
        Client client = getClient();
        Response response = client.target(url).request().accept(MediaType.APPLICATION_JSON_TYPE)
                .buildPost(Entity.entity(domain, MediaType.APPLICATION_JSON_TYPE))
                .invoke();
       // String str = response.readEntity(String.class);
        EnterpriseEmailRs rs;
        String data = "";
        try {
            data = response.readEntity(String.class);
            rs = objectMapper.readValue(data.getBytes(), EnterpriseEmailRs.class);
        } catch (Exception e) {
            log.error("email exception {}", data);
            throw new ServiceException(SecurityError.ENTERPRISE_EMAIL_ERROR);
        }
        closeClient(client);
        return rs.getData();
    }

简单来说就是api使用简单,很舒服,但是需要和jersey一起来使用,没有单独拿出来进行http的请求。

2.jersey代理连接池


以上说了这么多的http框架,但是在使用的时候要实例化client或者从连接池里面拿出连接,进行各种配置,指定url,参数,接收数据类型等等,虽说可以讲所有的GET、POST、PUT方法的调用加上泛型修饰,但是未免有些臃肿,但也确实是有比较好的方法,我只写出接口,然后进行一定程度的配置后,调用这些接口就会去请求远程的服务,jersey确实是可以做到,在我所在公司,以在产品越来越大,进行模块拆分独立部署的时候,相互之间的内部调用的方法便变成了内网http请求,在这虽说更好的方法是使用rpc框架来进行模块之间的通信,但是对于我们的产品来说使用rest通信,然后进行一定程度的性能调优之后,还是可以接受的,因此便一直采用了这种通信方法。

jersey内置一个WebResourceFactory类,我们再使用的时候,注册webTarget,指定远程调用地址、编写的接口,然后使用的时候从resources中取出对应注册的接口,调用的时候,就是调用到远程服务,这个整个的配置十分复杂,要完全讲述下来需要搭一套完整的以jersey为rest框架的应用程序,基本上可以抽出来单独写一个系列了,这个有疑问的可以私信我单独讨论。

3. 综上


上述讲了多种http框架的使用,有的难用,有的易用,有的功能十分强大,支持各种特性、有的使用依赖于一个完整的rest框架,如jersey-client和jersey代理连接池等等,但是讲述以上内容并不是说对这几个http框架进行一个对比,而是要在spring cloud搭建的系统中使用一款http框架来进行服务间的调用,这一片是对我使用的http的框架的一个总结,下一篇将要讲述spring cloud中的feign,以一种十分优雅的形式调用远程服务,并且可以选择底层调用的http的框架是HttpClient还是OkHttp,这个在下一篇文章中将要展开。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐