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

标签 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 连接?

谢谢

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.URL 实例创建的 java.net.URLConnection 实例将创建多个到服务器的套接字。相反,为了简单起见,请在 synchronized block 中调用 URL.openConnection()同样,这并不意味着每次调用 URL.openConnection() 都会创建一个新的套接字。我相信 URL 对此进行了规范。我错了。创建的套接字数量取决于调用 URL.openConnection() 的线程数量。
  5. 在很多地方都提到过,我再次提到,关闭/断开 URLConnection 不会关闭套接字。
  6. 连接到同一服务器的不同路径不会创建另一个连接套接字。换句话说,持久连接可用于不同的路径。
  7. Apache HTTPClient 通常更易于使用且更直观。它支持多线程环境中的持久连接(使用相同的套接字进行连接),无需用户干预。
  8. 我无法获取符合 http.maxConnectionshttp.keepAliveURL 。例如,我在运行时包含 -Dhttp.keepAlive=false 不会阻止 Connection: keep-alive 包含在 HTTP header 中。

我的观察来自于我粘贴的 here 示例。它们是比上面粘贴的代码更好的示例。

最佳答案

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

  1. URLConnection 速度很快,因为它为每个线程与服务器建立的每个连接打开新的套接字,而 Apache HTTPClient 根据多线程环境中的设置控制打开的套接字数量。当套接字限制为少数时,两个 HTTP 库所花费的总连接时间大致相同。
  2. Apache HTTPClient 对不同的 URL 使用与同一服务器的持久连接。

mitmproxy是一个很好且易于使用的 HTTP 连接验证工具。

关于java - Apache HTTPClient 4.x 与多个 JDK URL 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42995772/

相关文章:

java - HttpClient 4.3.x,修复不推荐使用的代码以使用当前的 HttpClient 实现

Java - 初始化数组后和使用类方法测试数组时出现 NullPointerException

java - 短字符串的哈希码可以相同吗?

java - 如何在我的应用程序中实现语音转文本

执行 Apache HttpClient 时出现 java.lang.InterruptedException

Java http 客户端和 POODLE

java - Android项目使用httpclient --> http.client (apache), post/get方法

java - 呃哦 - 应用程序意外停止

java - 带有 if 语句的嵌套循环的时间复杂度 O(N) : O(N^4)?

java - 使用 Apache HttpClient 通过 SSL 身份验证发送 HTTP 请求