Apache HTTPClient 4.x与多个JDK URL连接

时间:2017-03-24 09:29:38

标签: java apache-httpclient-4.x

我正在尝试使用Apache HTTPClient 4.3.6连接池管理器来增加HTTP调用的吞吐量。我的假设是,HTTPClient实现通常使用持久性连接。但是,我的测试代码(包含在最后)的结果表明,使用JDK URLConnection的多个并发HTTP连接的性能更好。

  1. 如何快速HTTPClient
  2. HTTPClient http://localhost:9000/user/123http://localhost:9000/user/456使用相同的HTTP连接吗?
  3. 由于

    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.protocol.HttpClientContext;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    
    public class FooTest {
    
    public static void main(String[] args) throws Exception {
        runWithConnectionPool();
    }
    
     private static String extract(BufferedReader reader) throws Exception {
         StringBuilder buffer = new StringBuilder();
         String line = null;
         while ((line = reader.readLine()) != null) {
             buffer.append(line);
         }
         return buffer.toString();
     }
    
     private static void runWithConnectionPool() throws Exception {
         PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
         cm.setMaxTotal(1);
    
         CloseableHttpClient httpClient = HttpClients.custom()
             .setConnectionManager(cm)
             .setMaxConnTotal(100)
             .setMaxConnPerRoute(100)
             .build();
    
         long start = System.currentTimeMillis();
    
         HttpGet getReq = new HttpGet("http://www.google.com");
    
         /*
           Option A: Using HTTP connection pool
           Option B: Individual JDK 8 URL connection
         */
     //    Thread[] workers = generateAndStart(10, httpClient, getReq, 0);                 // (A)
         Thread[] workers = generateAndStart(10, getReq.getURI().toURL(), 0);  // (B)
    
         for (int i = 0; i < workers.length; i++) {
             workers[i].join();
         }
    
         System.out.println("Elasped: " + (System.currentTimeMillis() - start));
     }
    
     private static Thread[] generateAndStart(int num, URL url, long delay) {
         Thread[] workers = new Thread[num];
         for (int i = 0; i < num; i++) {
             System.out.println("Starting worker: " + i);
             int j = i;
             workers[i] = new Thread(() -> connect(url, delay, j));
             workers[i].start();
         }
         return workers;
     }
    
     private static void connect(URL url, long delay, int ndx)  {
         try {
             System.out.println(url.toURI().toString() + " started.");
         } catch (Exception e) {
             e.printStackTrace();
         }
    
         try {
    
             URLConnection connection = url.openConnection();
             connection.addRequestProperty("Accept", "application/json");
             BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    
             String line;
             while ((line = reader.readLine()) != null) {
                 ObjectMapper mapper = new ObjectMapper();
                 System.out.println(line);
             }
             if (delay > 0) {
                 System.out.println("Delayed.");
                 sleep(delay);
             }
             reader.close();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
    
     private static Thread[] generateAndStart(int num, CloseableHttpClient httpClient, HttpGet getReq, long delay) {
         Thread[] workers = new Thread[num];
         for (int i = 0; i < num; i++) {
             System.out.println("Starting worker: " + i);
             final int j = i;
             workers[i] = new Thread(() -> connect(httpClient, getReq, delay, j));
             workers[i].start();
         }
         return workers;
     }
    
     private static void connect(CloseableHttpClient httpClient, HttpGet request, long delay, int ndx) {
         System.out.println(request.getURI().toString() + " started.");
    
         try(
             CloseableHttpResponse response = httpClient.execute(request, HttpClientContext.create());
             BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
    
             String line;
             while ((line = reader.readLine()) != null) {
                 ObjectMapper mapper = new ObjectMapper();
                 System.out.println(line);
             }
             if (delay > 0) {
                 System.out.println("Delayed.");
                 sleep(delay);
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
    
     private static void sleep(long delay) {
         try {
             Thread.sleep(delay);
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
     }
    

    更新1(2017年3月28日)

    我已经提出了一些意见和结论,

    1. JDK java.net.URLConnection在调用URLConnection.getInputStream()之前不会建立连接。
    2. 如果连接错误,
    3. java.net.URLConnection会关闭当前套接字,例如HTTP错误,并创建一个新套接字。
    4. 在多线程环境中使用从同一java.net.URLConnection实例创建的java.net.URL实例将为服务器创建多个套接字。相反,为简单起见,请在URL.openConnection()块中调用synchronized同样,并不意味着每次调用URL.openConnection()都会创建一个新套接字。我相信URL会对此作出规定。我错了。创建的套接字数量取决于调用URL.openConnection()的线程数。
    5. 在很多地方都提到过,我再次提到,关闭/断开URLConnection并不会关闭套接字。
    6. 连接到同一服务器的不同路径不会创建另一个连接套接字。换句话说,持久连接可用于不同的路径。
    7. Apache HTTPClient通常更易于使用且更直观。它支持多线程环境中的持久连接(使用相同的套接字进行连接),无需用户干预。
    8. 我无法URL遵守http.maxConnectionshttp.keepAlive。例如,我在运行时包含-Dhttp.keepAlive=false并不会阻止HTTP标头中包含Connection: keep-alive
    9. 我的观察来自我粘贴的例子here。它们是比上面粘贴的代码更好的例子。

1 个答案:

答案 0 :(得分:0)

我在尝试使用JDK URLConnection和Apache HTTPClient后找到了答案。

  1. URLConnection很快,因为它为每个线程向服务器建立的每个连接打开新的套接字,而Apache HTTPClient根据多线程环境中的设置控制套接字的数量。当套接字限制为少时,两个HTTP库的总连接时间大致相同。
  2. Apache HTTPClient对不同的URL使用持久连接到同一服务器。
  3. mitmproxy是一个用于HTTP连接验证的好用且易于使用的工具。