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

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

  • 这篇文章中对acceptCount的分析其实很到位,在理解了tcp的握手过程、syn队列和accept队列的知识之后,就比较容易理解这个参数的含义以及在对tomcat的缓冲和保护作用了。
  • 在看了tomcat的源码之后,也容易理解文章中说的关于connectionTimeout其实就是SO_TIMEOUT也是对的,作为服务端的connectionTimeout这个参数很容易被名字误导跟一些客户端连接工具(比如HttpClient、数据库连接池之类)的connectionTimeout参数搞混,客户端这个参数代表英文直接翻译过来的意思“建立连接超时”,尝试跟对端建立连接然后尝试了这个时间之后还没连上就抛异常,而tomcat作为服务端的这个参数跟“建立连接”没关系,事实上它应该是建立连接之后,如果超过connectionTimeout这个时间还没收到客户端的请求,则抛异常。tomcat会默认用它来设置socket的readTimeout和writeTimeout、从这也可以看出tomcat中这个参数的含义。另外,这个参数跟maxKeepaliveTimeout的区别是maxKeepaliveTimeout强调是请求处理完了之后等待下一次请求的超时时间。
  • 文章中关于maxThreads的理解笔者也是认同的,关于tomcat工作线程池的大小如何调整其实取决于task的性质和当前系统运行状态CPU利用率,task阻塞比较多、可以适当调多一些线程个数增加并行处理和吞吐能力,如果task阻塞很少、cpu利用比较充分,那调多线程个数其实会起到相反效果、需知CPU在线程间切换的成本也是比较高的,当CPU花在线程切换上的开销甚至高于实际处理业务逻辑上的开销时,显然这样的调优是误入了歧途。

但是,文章中的一段关于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以高效利用长连接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容