Java HttpClient 连接池复用分析
Java HttpClient 连接池复用分析
- 场景描述
- 问题描述
- 日志分析
- 代码分析
- 解决
在Java 11中,HttpClient被添加进去。支持Http/1.1 和Http/2,并分别支持同步和异步调用两种方法。
上周使用HttpClient, 对一个后端服务进行压测,遇到了一些问题。
场景描述
场景比较简单,就是开200个线程,对后端服务接口进行压测,服务接口调用使用Java HttpClient。
问题描述
从后端服务监控看,RPS远不及预期,从LB的监控看,有数4000+的并发连接。这和实际的情况不符啊。
我开了200个线程,HttpClient的调用采用同步方式调用,按道理使用连接池,应该是200个并发链接才对,这里面一定有问题。
日志分析
先看下客户端日志吧,有大量的日志如下,
应该是创建的连接过多,创建新的tcp连接失败,再使用netstat 看下tcp连接的情况,
很快就达到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。
重新跑下,
可以看到连接数变成了200个,也不再报错了,并发请求量也提升了。