Servlet 3 性能调优

Servlet 3 早在 2011 年推出,但很多 web 项目还是使用 Servlet 2.5。这些 web 项目业务相对较重、单机负载相对较低,Servlet 2.5 由于更少切换线程比 Servlet 3 更加稳定。

随着微服务的发展,web 层不断演进,涌现出越来越多的 API 网关。API 网关将 web 层的业务剥离开,专注于安全防护和路由转发。这种重 IO 轻 CPU 的场景非常适合使用 Servlet 3。

通常异步 Servlet 项目中有 3 类线程池:Tomcat IO 工作线程池、业务线程池、RPC 线程池。

下面介绍 Servlet 优化的五个阶段,这五个阶段大致分为三类优化手段:线程隔离、无锁处理和更换容器。

第一阶段:纯同步

第一阶段采用同步 Servlet,Tomcat 线程进入业务代码并执行预处理、RPC 调用、结果处理
压测到 1000 QPS 时 AVG(20)、99 线(70)、999 线(75),整体平稳

第二阶段:伪异步

异步 Servlet 中通过 servletRequest.startAsync 开启异步,asyncContext.dispatch 重新分发请求、asyncContext.complete 结束异步。Spring MVC 的异步是先开启异步、执行业务、重新分发请求。这种方式是为了兼容 Spring MVC 的同步实现,但是由于两次分发导致代码更复杂、并可能对 Servlet 带来更大的压力。

在第二阶段的时候采用类似的实现:

  1. Tomcat 线程进入业务代码并预处理请求
  2. Tomcat 线程开启异步并异步请求 RPC 服务
  3. RPC 回调后重新分发请求
  4. Tomcat 线程进入业务代码并处理请求结果

压测到 1000 QPS 时 AVG(18)、99 线(56)、999 线(60),整体平稳,异步占优
压测到 1400 QPS 时 AVG(23)、99 线(80)、999 线(85),整体平稳,异步占优
压测到 2000 QPS 时 AVG(32)、99 线(300)、999 线(320),波动较大

第三阶段:全异步

项目在第三阶段的时候采用全异步:

  1. Tomcat 线程进入业务代码并开启异步
  2. Tomcat 线程提交业务线程并返回
  3. 业务线程预处理并调用 RPC 服务
  4. RPC 回调后提交业务线程
  5. 业务线程处理请求结果并结束异步

压测到 2500 QPS 时 AVG(44)、99 线(83)、999 线(85),整体平稳,时有抖动
压测到 3000 QPS 时 AVG(70)、99 线(180)、999 线(190),整体平稳,时有抖动

第四阶段:异步优化

业界 4 核 8 g 异步 Servlet 的机器吞吐量能够达到 1w+ QPS

1.观察机器在压测时的性能指标,QPS 3000 时出现 full gc

2.压测结束后 Dump 机器内存后发现无大对象,但是有大量 byte [] 和 char [] 对象

3.使用 JProfiler 分析 byte [] 的来源,可能来自 Tomcat 的 IO

在对比 Tomcat 线程数后猜测,可能是 Tomcat 线程池打满所致。同时在 Tomcat 日志中也出现大量队列已满的报错

4.尝试调整 Tomcat 线程池配置:增加线程数量、采用 Http11Nio2Protocol 处理协议。调整后压测吞吐量没有提升。https://renwole.com/archives/357

<Executor
 name="tomcatThreadPool"
 namePrefix="catalina-exec-"
 maxThreads="500"
 minSpareThreads="30"
 maxIdleTime="60000"
 prestartminSpareThreads = "true"
 maxQueueSize = "100"
/>
<Connector
 executor="tomcatThreadPool"
 port="8080"
 protocol="org.apache.coyote.http11.Http11Nio2Protocol"
 connectionTimeout="60000"
 maxConnections="10000"
 redirectPort="8443"
 enableLookups="false"
 acceptCount="100"
 maxPostSize="10485760"
 maxHttpHeaderSize="8192"
 compression="on"
 disableUploadTimeout="true"
 compressionMinSize="2048"
 acceptorThreadCount="2"
 URIEncoding="utf-8"
 processorCache="20000"
 tcpNoDelay="true"
 connectionLinger="5"
 server="Server Version 11.0"
 />

5.修改业务线程池大小
压测到 5000 QPS 时 AVG(59)、99 线(130)、999 线(145),整体平稳

6.修改调用下游服务的方式,基于回调 => 同步

7.对比业务各阶段耗时,增加服务调用线程池线程
压测到 6000 QPS 时高点 AVG(348)、99 线(500)、999 线(1000),整体平稳

8.异步 Servlet 开启异步后超时自动结束请求,RPC 在回调时如果已经超时会报状态异常,因此对 onTimeout 和 onComplete 加上同步锁

9.移除 RPC 调用,mock 服务返回结果
压测到 10000 QPS 时高点 AVG(12)、99 线(504)、999 线(1000),CPU 几乎打满

10.调整业务线程组,预处理和调用服务使用同一线程、RPC 回调和处理返回结果使用同一线程,弱化 CPU 轮转。压测到高点时开始出现线程阻塞
压测到 6000 QPS 时高点 AVG(8)、99 线(35)、999 线(1000),CPU 几乎打满

11.缩短 RPC 服务超时时间 200 -> 50
压测到 4000 时开始出现线程阻塞

12.移除 AsyncContext 监听中 onTimeout 和 onComplete 上的同步锁

13.使用 servlet 3.1 nio,注册 reader listener 和 write listener 异步读写

压测到 7000 QPS 时高点 AVG(10)、99 线(30)、999 线(1000)

第四阶段总结:

  1. 异步 IO 能一定程度上提高并发,在传输量不大的情况下提高并不明显
  2. 高并发同步调用会阻塞 IO 线程
  3. Tomcat 内部没有复用 bytebuffer 导致请求量大时新生代 gc 频繁,最后引起服务超时,日志阻塞等一系列问题

第五阶段:Jetty 容器

Jetty 底层对 ByteBuffer 对象的复用和线程池的处理有更好的优化。
在第五阶段,更换 Servlet 容器为 Jetty 进行调优。
压测到 11000 QPS 时高点 AVG(40)、99 线(90)、999 线(140),整体平稳

第五阶段总结:

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

推荐阅读更多精彩内容