2017-03-24 4 views
1

Apache HTTPClient 4.3.6接続プールマネージャを試して、HTTP呼び出しのスループットを向上させようとしています。私の前提は、HTTPClientの実装は一般的に永続性接続を使用していることです。しかし、私のテストコードの結果(最後に含まれています)では、JDK URLConnectionを使用した複数の同時HTTP接続のパフォーマンスが向上しています。Apache HTTPClient 4.xと複数のJDK URL接続

  1. HTTPClientを速くするにはどうすればよいですか?
  2. HTTPClienthttp://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. URLConnection.getInputStream()が呼び出されるまで、JDK java.net.URLConnectionは、接続を行うことはありません。
  2. java.net.URLConnection不適切な接続が発生した場合は現在のソケットを閉じます。 HTTPエラーを返し、新しいソケットを作成します。サーバに複数のソケットを作成するマルチスレッド環境で同じjava.net.URLインスタンスから作成されたjava.net.URLConnectionのインスタンスを使用して
  3. 。代わりに、簡略化のために​​ブロックのURL.openConnection()を呼び出してください。 同じノートでは、URL.openConnection()へのすべての呼び出しが新しいソケットを作成することを意味するものではありません。私はURLがこれを規制していると信じています。私は間違っていた。作成されるソケットの数は、URL.openConnection()を呼び出すスレッドの数に従います。
  4. URLConnectionを閉じたり切断したりしてもソケットを閉じることはありません。
  5. 同じサーバーの別のパスに接続しても、別の接続ソケットは作成されません。言い換えると、永続的な接続は異なるパスで使用可能です。
  6. 一般にApache HTTPClientは使いやすく、より直感的です。ユーザーの介入なしに、マルチスレッド環境で永続的な接続(接続に同じソケットを使用)をサポートします。
  7. 私はhttp.maxConnectionshttp.keepAliveに準拠しURLを取得できませんでした。たとえば、実行時に-Dhttp.keepAlive=falseを含めると、HTTPヘッダーに含まれているConnection: keep-aliveが防止されません。

私が貼り付けた例からの観察は、hereです。これらは、上に貼り付けられたコードよりも優れた例です。

+0

最大100個の接続を管理するようにプールを定義したため、プールが既に「テスト」のためにプールを利用しているかどうかはわかりませんが、プールによって各作業者に新しい接続が作成される可能性があります。このプールは、限られた数の接続(多くの場合、5つの同時アクセスに制限されるDB接続)を共有する必要がある場合、1つのHTTP 1.1に対して同じドメイン(同じ発信元ポリシー)へのHTTP接続の場合は8 Webアプリケーション) –

+0

接続ツールのネットトップから、10個ではなく1個の接続しか使用していないことがわかりました – thlim

+0

'java.net.HttpURLConnection'も接続プーリングを行っていますか?これには意味がないかもしれません。 – EJP

答えて

0

私はJDK URLConnectionとApacheのHTTPClientで実験した後に私の答えを見つけました。 ApacheのHTTPClientのは、マルチスレッド環境での設定に従ってオープンソケットの数を制御しながら、それがサーバーに各スレッドによって行われた接続ごとに新しいソケットを開くため

  1. URLConnectionは高速です。ソケットの数が限られている場合、両方のHTTPライブラリでほぼ同じになります。
  2. Apache HTTPClientは、異なるURLに対して同じサーバーへの永続的な接続を使用します。

mitmproxyは、HTTP接続の検証に便利で使いやすいツールです。

関連する問題