Apache HttpClient 4.3不会保持活着

时间:2014-01-23 03:30:51

标签: apache-httpclient-4.x keep-alive

在nGinx服务器端,为客户端设置connection-keep alive最多30秒。但是使用这个代码和附加的日志,HttpClient 4.3。并不尊重保持活力并在每次请求后关闭连接。为什么会这样?

我在重复尝试netstat -an并发现客户端在for循环执行期间的不同时间打开不同的端口并且FIN状态每2秒进行一次时得出了这个结论。

下面给出的日志还显示每次请求后关闭连接

static ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
            return 30 * 1000;
    }
};

public static void main(String[] args) throws Exception {

    java.util.logging.Logger.getLogger("org.apache.http.wire").setLevel(java.util.logging.Level.FINER);
    java.util.logging.Logger.getLogger("org.apache.http.headers").setLevel(java.util.logging.Level.FINER);

    System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
    System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
    System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug");
    System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http", "debug");
    System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http.headers", "debug");

    callServer();
}

public static void callServer() throws Exception {

    KeyStore keyStore = KeyStore.getInstance("pkcs12");

    FileInputStream instream = new FileInputStream(new File("/engineering/workspace/nginx_pilot/keystores/clientkeystore.pkcs"));
    try {
        keyStore.load(instream, "****".toCharArray());
    } finally {
        instream.close();
    }

    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());

    instream = new FileInputStream(new File("/engineering/workspace/nginx_pilot/keystores/serverKeystore.jks"));
    try {
        trustStore.load(instream, "****".toCharArray());
    } finally {
        instream.close();
    }

    SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, "pass".toCharArray()).loadTrustMaterial(trustStore).build();
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE).build();

    CloseableHttpResponse response = null;

    try {
        HttpGet httpget = new HttpGet("https://" + getString() + ":8443/GenevaServers/GetConfig/unit.test.asset.ast1");

        System.out.println("executing request" + httpget.getRequestLine());

        for (int i = 0; i < 10000; i++) {
            System.out.println("Beginning");
            long currentTimeMillis = System.currentTimeMillis();
            response = httpclient.execute(httpget);
            System.out.println((System.currentTimeMillis() - currentTimeMillis) + "ms");
            HttpEntity entity = response.getEntity();
            System.out.println(IOUtils.toString(entity.getContent()));
            EntityUtils.consume(entity);
            System.out.println("End");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        response.close();
        httpclient.close();
    }
}

这些是日志。

2014/01/22 19:24:50:252 PST [DEBUG] RequestAddCookies - CookieSpec selected: best-match
2014/01/22 19:24:50:252 PST [DEBUG] RequestAuthCache - Auth cache not set in the context
2014/01/22 19:24:50:252 PST [DEBUG] PoolingHttpClientConnectionManager - Connection      request: [route: {s}->https://ichor.corp.com:8443][total kept alive: 2; route allocated: 2 of 2; total allocated: 2 of 20]
2014/01/22 19:24:50:253 PST [DEBUG] DefaultManagedHttpClientConnection - http-outgoing-0:    Close connection
2014/01/22 19:24:50:254 PST [DEBUG] PoolingHttpClientConnectionManager - Connection leased: [id: 2][route: {s}->https://ichor.corp.com:8443][total kept alive: 1; route allocated:    2 of 2; total allocated: 2 of 20]
2014/01/22 19:24:50:254 PST [DEBUG] MainClientExec - Opening connection {s}-  >https://ichor.corp.com:8443
2014/01/22 19:24:50:254 PST [DEBUG] HttpClientConnectionManager - Connecting to    ichor.corp.com/17.169.1.18:8443
2014/01/22 19:24:50:392 PST [DEBUG] MainClientExec - Executing request GET /GenevaServers/GetConfig/unit.test.asset.ast1 HTTP/1.1
2014/01/22 19:24:50:392 PST [DEBUG] MainClientExec - Target auth state: UNCHALLENGED
2014/01/22 19:24:50:392 PST [DEBUG] MainClientExec - Proxy auth state: UNCHALLENGED
2014/01/22 19:24:50:392 PST [DEBUG] headers - http-outgoing-2 >> GET   /GenevaServers/GetConfig/unit.test.asset.ast1 HTTP/1.1
2014/01/22 19:24:50:392 PST [DEBUG] headers - http-outgoing-2 >> Host: ichor.corp.com:8443
2014/01/22 19:24:50:392 PST [DEBUG] headers - http-outgoing-2 >> Connection: Keep-Alive
2014/01/22 19:24:50:392 PST [DEBUG] headers - http-outgoing-2 >> User-Agent: Apache- HttpClient/4.3.1 (java 1.5)
2014/01/22 19:24:50:392 PST [DEBUG] headers - http-outgoing-2 >> Accept-Encoding: gzip,deflate
2014/01/22 19:24:50:392 PST [DEBUG] wire - http-outgoing-2 >> "GET /GenevaServers/GetConfig/unit.test.asset.ast1 HTTP/1.1[\r][\n]"
2014/01/22 19:24:50:392 PST [DEBUG] wire - http-outgoing-2 >> "Host: ichor.corp.com:8443[\r][\n]"
2014/01/22 19:24:50:392 PST [DEBUG] wire - http-outgoing-2 >> "Connection: Keep-Alive[\r][\n]"
2014/01/22 19:24:50:392 PST [DEBUG] wire - http-outgoing-2 >> "User-Agent: Apache-HttpClient/4.3.1 (java 1.5)[\r][\n]"
2014/01/22 19:24:50:392 PST [DEBUG] wire - http-outgoing-2 >> "Accept-Encoding: gzip,deflate[\r][\n]"
2014/01/22 19:24:50:393 PST [DEBUG] wire - http-outgoing-2 >> "[\r][\n]"
2014/01/22 19:24:50:462 PST [DEBUG] wire - http-outgoing-2 << "HTTP/1.1 200 OK[\r][\n]"
2014/01/22 19:24:50:462 PST [DEBUG] wire - http-outgoing-2 << "Server: nginx/1.4.1[\r][\n]"
2014/01/22 19:24:50:463 PST [DEBUG] wire - http-outgoing-2 << "Date: Thu, 23 Jan 2014 03:24:09 GMT[\r][\n]"
2014/01/22 19:24:50:463 PST [DEBUG] wire - http-outgoing-2 << "Content-Type: application/json[\r][\n]"
2014/01/22 19:24:50:463 PST [DEBUG] wire - http-outgoing-2 << "Content-Length: 270[\r][\n]"
2014/01/22 19:24:50:463 PST [DEBUG] wire - http-outgoing-2 << "Connection: keep-alive[\r][\n]"
2014/01/22 19:24:50:463 PST [DEBUG] wire - http-outgoing-2 << "[\r][\n]"

任何建议都会有很大的帮助。提前谢谢。

1 个答案:

答案 0 :(得分:5)

当然可以,但这是一个特例

SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, "pass".toCharArray()).loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

您正在使用SSL密钥对目标服务器进行身份验证。这使得连接代表特定用户并携带特定的安全上下文。换句话说,连接具有特定状态(用户主体)。您很可能不希望将此连接租给可能代表完全不同用户的任意执行线程,对吗? HttpClient 4(与其前身不同)跟踪有状态连接,并确保它们不能被具有不同执行上下文的线程重用。

有几种方法可以解决问题

  1. 推荐。使所有逻辑相关的请求共享same execution context

  2. 手动manage connection state

  3. 禁用连接状态跟踪。这项措施会产生安全隐患,因此必须谨慎考虑