tomcat

keepalive连接复用对tomcat线程池的影响

2021-08-02  本文已影响0人  肥兔子爱豆畜子

本文来源于看到的一篇文章: tomcat的acceptCount、maxThreads、connectionTimeout参数调整

但是,文章中的一段关于keepalive的观点让我产生了疑惑:

“当开启http keep alive的时候,client端可能没有那么及时地关闭连接,那么server端的worker线程会一直被这些实际上可能不活跃的连接给占用了,导致worker线程没能重复利用起来。”

如果按照上面的描述,当client没有请求发送但维持住长连接不及时关闭,tomcat的worker线程会被占用,那不就是相当于:

  1. 在tomcat使用nio模式时,client与tomcat之间维持连接,复用于多个request,这时候worker线程是专门为这个连接服务的吗?连接断开之前能否服务其他连接?
  2. 如果不能,那么是不是就跟bio一样了,相当于有多少个worker线程就能服务多少个连接了?


直觉感觉这不可能,事实上,tomcat NIO模式下,worker线程在一次request的读写过程中是blocking的,但是一次request读写完成之后,等待下次request是unblocking的。这在tomcat的官网上关于其几种IO模式的设计思路上可以查到。见下图:

另外,关于tomcat的IO方式和线程模型笔者也在Tomcat NIO线程模型与IO方式分析 这篇文章中分析过:等待下一次从socket连接过来的请求的时候是把socket注册到Poller的selector上的,这个时候它是非阻塞的,而读一个具体的request body的时候,如果body未读完则使用CountDownLatch阻塞当前线程等待BlockPoller通知继续读。

Poller每次提交给worker线程池的task类是SocketProcessor,名字起的有些误导人,事实上提交给线程池处理的不是“一个连接”而应该理解为“一次请求”,同一个连接上可以有多个请求,每个请求可能会分配给不同的worker线程去处理,但是每个请求的body是始终由一个线程处理的。这就是tomcat的请求处理的线程模型。

程序验证

服务端使用springboot2内嵌的tomcat9运行一个servlet,为了验证,对tomcat做如下配置:

server.port=8080
server.servlet.context-path=/prototype
server.tomcat.max-threads=1
server.tomcat.max-connections=6
server.tomcat.accept-count=2

然后启动两个客户端程序,代码基本一样,就是不断的以keepalive的方式往服务端发请求:

import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 使用HttpClient fluent api客户端工具建立连接并发送http POST请求,
 * 客户端有连接池,能够以keepalive方式与服务端进行连接复用,一个连接可发送多个请求。
 * */
public class TestHttpKeepAlive1 {
    private static Logger logger = LoggerFactory.getLogger(TestHttpKeepAlive1.class);

    public static void main(String[] args) {
        StringEntity entity = new StringEntity("{\"name\":\"AAA\"}", ContentType.APPLICATION_JSON);
        try {
            while (true) {
                String reponseContent = Request.Post("http://localhost:8080/prototype/testRequestServlet").body(entity)
                        .execute().returnContent().toString();
                logger.info(reponseContent);
                TimeUnit.SECONDS.sleep(2);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

tomcat的默认maxKeepaliveTimeout是60s,另外一个控制长连接能保持多久的参数是maxKeepAliveRequests、默认是100,可以通过调试客户端程序配合netstat命令查看tcp连接,当启动一个客户端向服务端发送100次请求之前或发送完一个请求之后的60秒内,客户端与服务端之间的连接一直是同一个,这可以通过客户端的端口号确认。


而当我们启动两个客户端的时候,可以看到服务端也是可以用1个工作线程来同时服务两个客户端连接的,且2个连接一直保持(执行了100次请求之后会打开新连接)。



连接复用可以较少频繁的连接建立与关闭带来的开销,特别是对于传输报文本身比较小的情况,短链接的建立与关闭开销在整个通信过程占比十分可观。但有一点要注意就是长连接对客户端出站端口的占用和服务端tomcat连接数的占用(通过LimitLatch控制),当tomcat仅接收来自其他内部系统的调用的时候、客户端连接的数量相对是可控的,开启长连接可以显著提高性能。

进一步参考

关于tomcat的各个配置参数的含义和默认值,可以参考tomcat官网doc: https://tomcat.apache.org/tomcat-9.0-doc/config/http.html
通过合理的使用keepalive来提高client与tomcat之间的http请求性能,可以参考配置TOMCAT及httpClient的keepalive以高效利用长连接

上一篇下一篇

猜你喜欢

热点阅读