欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: 行思錄

Java HTTP 组件库选型看这篇就够了

最近项目需要利用 Java 重度挪用 HTTP API 接口,于是想着封装一个团队公用的 HTTP client lib. 这个库需要支持以下特性:

  1. 毗连池打点,包罗毗连建设和超时、空闲毗连数节制、每个 host 的毗连数设置等。根基上,我们想要一个 go HTTP 尺度库自带的毗连吃打点成果。
  2. 域名理会节制。因为挪用量会较量大,因此但愿在域名理会这一层做一个挪用端可控的负载平衡,同时可以对每个处事器 IP 举办失败率统计和康健度查抄。
  3. Form/JSON 挪用支持精采。
  4. 支持同步和异法式用。

在 Java 生态中,固然有数不清的 HTTP client lib 组件库,可是概略可以分为这三类:

  1. JDK 自带的 HttpURLConnection 尺度库;
  2. Apache HttpComponents HttpClient, 以及基于该库的 wrapper, 如 Unirest.
  3. 非基于 Apache HttpComponents HttpClient, 大量重写应用层代码的 HTTP client 组件库,典范代表是 OkHttp.

HttpURLConnection

利用 HttpURLConnection 提倡 HTTP 请求最大的利益是不需要引入特另外依赖,可是利用起来很是繁琐,也缺乏毗连池打点、域名机器节制等特性支持。以提倡一个 HTTP POST 请求为例:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;


public class HttpUrlConnectionDemo {

    public static void main(String[] args) throws Exception {
        String urlString = "https://httpbin.org/post";
        String bodyString = "password=e10adc3949ba59abbe56e057f20f883e&username=test3";

        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        OutputStream os = conn.getOutputStream();
        os.write(bodyString.getBytes("utf-8"));
        os.flush();
        os.close();

        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            InputStream is = conn.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            System.out.println("rsp:" + sb.toString());
        } else {
            System.out.println("rsp code:" + conn.getResponseCode());
        }
    }

}

可以看到,利用 HttpURLConnection 提倡 HTTP 请求是较量原始(low level)的,根基上你可以领略为它就是对网络栈传输层(HTTP 一般为 TCP,HTTP over QUIC 是 UDP)举办了一次浅条理的封装,操纵原语就是在打开的毗连上面写请求 request 与读响应 response. 并且 HttpURLConnection 无法支持 HTTP/2. 显然,官方是知道这些问题的,因此在 Java 9 中,官方在尺度库中引入了一个 high level、支持 HTTP/2 的 HttpClient. 这个库的接口封装就很是主流到位了,提倡一个简朴的 POST 请求:

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyProcessor.fromString("Sample request body"))
  .build();

封装的最大特点是链式挪用很是顺滑,支持毗连打点等特性。可是这个库只能在 Java 9 及今后的版本利用,Java 9 和 Java 10 并不是 LTS 维护版本,而接下来的 Java 11 LTS 要在2018.09.25宣布,应用到线上还需要期待一段时间。因此,固然挺喜欢这个自带尺度库(究竟可以不引入三方依赖),但当前是无法在出产情况利用的。

Apache HttpComponents HttpClient

Apache HttpComponents HttpClient 的前身是 Apache Commons HttpClient, 可是 Apache Commons HttpClient 已经遏制开拓,假如你还在利用它,请切换到 Apache HttpComponents HttpClient 上来。

Apache HttpComponents HttpClient 支持的特性很是富厚,完全包围我们的需求,利用起来也很是顺手:

<br />import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.impl.client.HttpClients;


public class HttpComponentsDemo {
    final static CloseableHttpClient client = HttpClients.createDefault();

    // 通例挪用
    private String sendPostForm(String url, Map&lt;String, String&gt; params) throws Exception {
        HttpPost request = new HttpPost(url);

        // set header
        request.setHeader("X-Http-Demo", HttpComponentsDemo.class.getSimpleName());

        // set params
        if (params != null) {
            List&lt;NameValuePair&gt; nameValuePairList = new ArrayList&lt;NameValuePair&gt;();
            for (Map.Entry&lt;String, String&gt; entry : params.entrySet()) {
                nameValuePairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            UrlEncodedFormEntity bodyEntity = new UrlEncodedFormEntity(nameValuePairList, "UTF-8");
            //System.out.println("body:" + IOUtils.toString(bodyEntity.getContent()));
            request.setEntity(new UrlEncodedFormEntity(nameValuePairList));
        }

        // send request
        CloseableHttpResponse response = client.execute(request);
        // read rsp code
        System.out.println("rsp code:" + response.getStatusLine().getStatusCode());
        // return content
        String ret = readResponseContent(response.getEntity().getContent());
        response.close();
        return ret;
    }

    // fluent 链式挪用
    private String sendGet(String url) throws Exception {
        return Request.Get(url)
                .connectTimeout(1000)
                .socketTimeout(1000)
                .execute().returnContent().asString();
    }

    private String readResponseContent(InputStream inputStream) throws Exception {
        if (inputStream == null) {
            return "";
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buf = new byte[512];
        int len;
        while (inputStream.available() &gt; 0) {
            len = inputStream.read(buf);
            out.write(buf, 0, len);
        }

        return out.toString();
    }

    public static void main(String[] args) throws Exception {
        HttpComponentsDemo httpUrlConnectionDemo = new HttpComponentsDemo();
        String url = "https://httpbin.org/post";
        Map&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
        params.put("foo", "bar中文");
        String rsp = httpUrlConnectionDemo.sendPostForm(url, params);
        System.out.println("http post rsp:" + rsp);

        url = "https://httpbin.org/get";
        System.out.println("http get rsp:" + httpUrlConnectionDemo.sendGet(url));
    }
}