Java HttpClient 连接池复用分析

Java HttpClient 连接池复用分析

    • 场景描述
    • 问题描述
    • 日志分析
    • 代码分析
    • 解决
    • 总结

在Java 11中,HttpClient被添加进去。支持Http/1.1 和Http/2,并分别支持同步和异步调用两种方法。
上周使用HttpClient, 对一个后端服务进行压测,遇到了一些问题。

场景描述

场景比较简单,就是开200个线程,对后端服务接口进行压测,服务接口调用使用Java HttpClient。
Java HttpClient 连接池复用分析_第1张图片

问题描述

从后端服务监控看,RPS远不及预期,从LB的监控看,有数4000+的并发连接。这和实际的情况不符啊。
我开了200个线程,HttpClient的调用采用同步方式调用,按道理使用连接池,应该是200个并发链接才对,这里面一定有问题。
Java HttpClient 连接池复用分析_第2张图片

日志分析

先看下客户端日志吧,有大量的日志如下,
Java HttpClient 连接池复用分析_第3张图片

应该是创建的连接过多,创建新的tcp连接失败,再使用netstat 看下tcp连接的情况,
Java HttpClient 连接池复用分析_第4张图片
很快就达到16000多个连接。这说明连接池肯定没有复用。

有两种可能,一种是用完的连接没有回收到连接池,一种是请求的时候没有从连接池中获取到可复用的连接。

代码分析

单步调试下,在
HttpConnection::getConnection()中,

   if (!secure) {
        c = pool.getConnection(false, addr, proxy);
        if (c != null && c.checkOpen() /* may have been eof/closed when in the pool */) {
            final HttpConnection conn = c;
            if (DEBUG_LOGGER.on())
                DEBUG_LOGGER.log(conn.getConnectionFlow()
                                 + ": plain connection retrieved from HTTP/1.1 pool");
            return c;
        } else {
            return getPlainConnection(addr, proxy, request, client);
        }
    } else {  // secure
        if (version != HTTP_2) { // only HTTP/1.1 connections are in the pool
            c = pool.getConnection(true, addr, proxy);
        }
        if (c != null && c.isOpen()) {
            final HttpConnection conn = c;
            if (DEBUG_LOGGER.on())
                DEBUG_LOGGER.log(conn.getConnectionFlow()
                                 + ": SSL connection retrieved from HTTP/1.1 pool");
            return c;
        } else {
            String[] alpn = null;
            if (version == HTTP_2 && hasRequiredHTTP2TLSVersion(client)) {
                alpn = new String[] { "h2", "http/1.1" };
            }
            return getSSLConnection(addr, proxy, alpn, request, client); // 跑到这了
        }
    }

跑到了最后,协议走的是http/2。 协议如果走的http/2的话,没有从连接池中获取可复用连接。

解决

那么,显示的指定下http协议版本1.1。
重新跑下,
Java HttpClient 连接池复用分析_第5张图片
可以看到连接数变成了200个,也不再报错了,并发请求量也提升了。

总结

  • 一定要显示的声明协议版本,否则缺省是http/2(支持ssl情况下);
  • 如果是http/2,不会使用连接池,导致创建过多的连接,有可能将客户端和服务器都打死。

你可能感兴趣的:(java,开发语言,网络)