Tomcat 、Jetty 和 Undertow 对比测试

背景


最近在做压测 , 同事觉得 Tomcat 不行想要切换 web 服务器 , 于是我就去网上搜了一些测评结果 , 不出意外 Tomcat 得到了一致的差评。抱着怀疑的态度 , 我决定对 SpringBoot 内嵌支持的三款 Servlet 容器 ( TomcatJettyUndertow ) 做一个简单的对比测试 , 测试结果仅供参考

测试环境


项目 描述
测试端主机 阿里云ECS(ecs.sn1ne.2xlarge) 8C16G CentOS7.4.1708 Linux Kernel 3.10.0
被测端主机 阿里云ECS(ecs.sn1ne.2xlarge) 8C16G CentOS7.4.1708 Linux Kernel 3.10.0
网络环境 阿里云专有网络 内网带宽: 2 Gbps
测试工具 wrk
项目框架 SpringBoot 1.5.10.RELEASE
JDK 1.8.0_191 Java HotSpot(TM) 64-Bit Server VM
测试容器 Tomcat Jetty Undertow

说明:

  • 测试端和被测端分别在两台主机上 , 为了避免放在一台主机上导致互相抢占 CPU 而无法得到真实结果。

  • 主角是 Servlet容器 , 所以网络环境不应该是这次测试的瓶颈 ( 系统内核的 TCP 参数也要优化 ) 。

  • 测试工具用过 wrkabjmeter , 测试的目的是为了获取被测端的性能 , 但不能因为测试工具的性能瓶颈而埋没了被测端的真实能力 , 因此选用简单高效的 wrk ( 下载源码编译安装 ) 。

测试方法


使用最简单的 HTTP 接口 , 不包含任何业务逻辑和数据库操作 , 反映容器的极致性能

@RestController
@RequestMapping("/api")
public class TestController {

    @RequestMapping("/test")
    public String test() {
        return "This is a test result.";
    }

}

使用 wrk 5000 并发下持续压测 10 分钟 ( 超时时间设为 30s , 避免超时时间过短带来的大量错误 )

./wrk -t 8 -c 5000 -d 10m -T 30s "http://yourhostip:3000/api/test"

说明:

  • JVM 内存设置为 2G 大小

  • 项目启动后需要预热 ( JIT 编译、加线程等等 ) 一下才能达到最优的性能。

  • 为了避免性能数据波动造成的影响 , 每种情况测试 3 遍后取最优结果。

  • 按理说测试时间越长越能反映真实情况,不过试了 30 分钟的测试 , 一趟测试下来耗费了一天时间 , 结果和 10 分钟的也差不了多少 , 所以还是节省点时间吧。

测试结果


Tomcat 8.0.53 NIO 模式

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -jar servlet-test-tomcat-8-nio.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 161.3% ( 最大 800% )
    Server 的 CPU 使用率: 52.4 us, 13.0 sy,  0.0 ni, 16.8 id,  0.0 wa,  0.0 hi, 17.8 si,  0.0 st
    Server GC 情况:YGC: 870 YGCT: 5.3 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    86.31ms    7.23ms 778.24ms   88.50%
        Req/Sec     7.25k   420.33    14.55k    72.93%
      34639518 requests in 10.00m, 5.33GB read
      Socket errors: connect 0, read 1253, write 2928, timeout 0
    Requests/sec:  57726.28
    Transfer/sec:      9.09MB
    

Tomcat 8.5.34 NIO 模式

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -jar servlet-test-tomcat-8.5-nio.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 132.3% ( 最大 800% )
    Server 的 CPU 使用率: 60.2 us, 14.1 sy,  0.0 ni, 19.1 id,  0.0 wa,  0.0 hi,  6.5 si,  0.0 st
    Server GC 情况:YGC: 940 YGCT: 5.736 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    83.43ms    9.79ms   1.70s    96.22%
        Req/Sec     7.51k   408.60    14.48k    73.85%
      35852565 requests in 10.00m, 4.55GB read
      Socket errors: connect 0, read 1165, write 2883, timeout 0
    Requests/sec:  59745.50
    Transfer/sec:      7.76MB
    

Tomcat 8.0.53 APR 模式 ( 服务器上需要安装 apr 类库 )

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -Djava.library.path=/usr/local/apr/lib -jar servlet-test-tomcat-8-apr.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 183.4% ( 最大 800% )
    Server 的 CPU 使用率: 69.5 us, 13.7 sy,  0.0 ni,  3.7 id,  0.0 wa,  0.0 hi, 13.1 si,  0.0 st
    Server GC 情况:YGC: 1224 YGCT: 6.251 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency    61.71ms   71.59ms   7.29s    99.87%
        Req/Sec    10.36k   441.68    18.63k    78.23%
      49480257 requests in 10.00m, 7.61GB read
      Socket errors: connect 0, read 1388, write 814, timeout 0
    Requests/sec:  82453.62
    Transfer/sec:     12.99MB
    

Jetty 9.4.8.v20171121

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -jar servlet-test-jetty.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 11.3% ( 最大 800% )
    Server 的 CPU 使用率: 98.6 us,  0.5 sy,  0.0 ni,  0.5 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
    Server GC 情况:YGC: 640 YGCT: 6.541 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   126.01ms  435.50ms  29.95s    90.64%
        Req/Sec   206.88    127.12     1.03k    70.75%
      978373 requests in 10.00m, 128.76MB read
      Socket errors: connect 0, read 95571, write 7191, timeout 331
    Requests/sec:   1630.39
    Transfer/sec:    219.72KB
    

Undertow 1.4.22.Final

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -jar servlet-test-undertow.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 32.6% ( 最大 800% )
    Server 的 CPU 使用率: 93.6 us,  2.4 sy,  0.0 ni,  1.5 id,  0.0 wa,  0.0 hi,  2.5 si,  0.0 st
    Server GC 情况:YGC: 370 YGCT: 1.684 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   354.95ms  138.95ms   1.93s    75.08%
        Req/Sec     1.77k   199.04     4.19k    72.85%
      8471058 requests in 10.00m, 1.28GB read
      Socket errors: connect 0, read 1601, write 2882, timeout 0
    Requests/sec:  14117.10
    Transfer/sec:      2.18MB
    

Undertow 1.4.22.Final 配置线程数

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -Dserver.undertow.io-threads=16 -Dserver.undertow.worker-threads=256 -jar servlet-test-undertow.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 33.3% ( 最大 800% )
    Server 的 CPU 使用率: 92.1 us,  2.4 sy,  0.0 ni,  2.3 id,  0.0 wa,  0.0 hi,  3.2 si,  0.0 st
    Server GC 情况:YGC: 367 YGCT: 2.23 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   391.49ms  333.83ms   3.23s    60.47%
        Req/Sec     1.76k   235.55     4.51k    69.41%
      8389131 requests in 10.00m, 1.27GB read
      Socket errors: connect 0, read 3987, write 3486, timeout 0
    Requests/sec:  13980.36
    Transfer/sec:      2.16MB
    

Undertow 2.0.15.Final

  • 启动命令:

    java -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k -jar servlet-test-undertow2.jar
    
  • 监控项:

    wrk 的 CPU 使用率: 31.6% ( 最大 800% )
    Server 的 CPU 使用率: 90.0 us,  2.6 sy,  0.0 ni,  4.1 id,  0.0 wa,  0.0 hi,  3.3 si,  0.0 st
    Server GC 情况:YGC: 361 YGCT: 2.676 FGC:0
    
  • wrk 结果:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   368.09ms   17.14ms 942.66ms   97.28%
        Req/Sec     1.70k   164.17     4.01k    76.01%
      8118614 requests in 10.00m, 1.22GB read
      Socket errors: connect 0, read 686, write 1453, timeout 0
    Requests/sec:  13530.04
    Transfer/sec:      2.09MB
    

测试结果分析


Servlet 容器 QPS QPS / GC 次数
Tomcat 8.0.53 NIO 模式 57726.28 57726.28 / 870 = 66.352
Tomcat 8.5.34 NIO 模式 59745.50 59745.50 / 940 = 63.559
Tomcat 8.0.53 APR 模式 82453.62 ( 最优 ) 82453.62 / 1224 = 67.364
Jetty 9.4.8.v20171121 1630.39 ( 最差 ) 1630.39 / 640 = 2.547
Undertow 1.4.22.Final 14117.10 14117.10 / 370 = 38.154
Undertow 1.4.22.Final 配置线程数 13980.36 13980.36 / 367 = 38.094
Undertow 2.0.15.Final 13530.04 13530.04 / 361 = 37.479
  • 先说结果: Tomcat APR 模式 "最优" , Jetty "最差" , Undertow 也没有想象中的碾压 Tomcat

  • Tomcat NIO 模式 压力刚上来的时候 CPU 会用满 , 稳定后基本会留有 10% 以上的空闲

  • Jetty 采用的默认配置 , 才 2k 不到的 QPS 确实有点出乎意料的 , 需要配置后做进一步的测试对比

  • Undertow 提供的配置项很少 , 只有 线程数缓冲大小 , 加大 线程数 后也没能提升性能。本次测试中 Undertow2.0 版本也没有性能上的提升。

  • Tomcat8.5 相对于 Tomcat8.0 应该有性能上的提升 , 由于系统中安装的 apr 库的版本问题没有测试 Tomcat8.5+APR 的性能 ( Tomcat 版本和 apr 版本要对应 )

  • 默认线程数: Undertow8 ( CPU 核心数 ) 个 IO 线程 + 64 ( 8 * IO 线程数 ) 个 WORK 线程, TomcatJetty 均在 200 左右。因为都用的 NIO , 所以调大线程数效果不大 , 反而带来 CPU上下文切换内存消耗 ( -Xss ) 的问题

  • 听人说 Undertow 在正常运行中会莫名其妙挂掉 , 具体原因还不清楚

优化


  • JVM 参数

    曾经我也比较信奉所谓的 标准参数 , 然而按照上述方法单独对 Tomcat 进行对比测试后并没有得到所谓的 "参数调优" 的效果, 所以还是 针对实际运行情况再做调整吧
    主要几类配置项有:

    1. -server

    2. 内存 ( -Xms -Xmx -Xmn -Xss )

    3. 垃圾回收器组合

    4. GC 相关参数 ( 打印日志 / DisableExplicitGC /ExplicitGCInvokesConcurrent / 定时 GC )

  • Connector 运行模式

    Tomcat ConnectorBIONIOAPR 三种运行模式 ( 可以看下这篇 文章 ) , 虽然 APR 模式性能最好 ( 不绝对 ) , 但也带来了维护上的成本 , 所以还是那句话 —— 针对实际的业务场景选择

  • 项目运行参数

    SpringBoot 中几个容器的可配置项都在 org.springframework.boot.autoconfigure.web.ServerProperties 这个类中, 一般默认就行了, 遇到问题再调整 ( Tomcat 相关配置可以看 这里 )

结语


  • 本次测试结果和大部分网上的测试正好相反

    网上的测试:在一台机器上跑 , 用例并发量比较小 , 得到的 QPS 也比较低 ( 几百的数值而且比较接近 ) ,也没有说明测试使用的机器配置和容器版本 , 只是简单的把 Tomcat 归类为性能最差。这样的测试结论我认为是很不可靠的。

    时间有限 , 以上只是部分的测试用例 , 实际可以组合的情况 ( 服务器配置、容器版本、实际业务代码等等 ) 多了去了 , 所以再强调一遍 —— 本次测试结果仅供参考

  • 有人可能会奇怪本次测试的意义?

    一般服务器上会部署多个应用 , 不会让一个应用把所有 CPU 都吃满 , 而且一般线上应用也不会是这么简单的接口 , 瓶颈往往是在一些复杂的业务逻辑和数据库上 , 网上也有提到三种容器使用的业务场景也有所不同。

    我想说的是 , 本次测试纯粹是测试容器的性能 , 空跑的结果都摆在这了 , 跑业务的话性能难道能更好?如果 Jetty 真的如测试中那么差 , 还会有人用它跑高并发的项目吗?

    所以 , 本文只是提供一种测试思路 , 有疑问就多动动手进行论证 , 在没有亲自实践过的情况下都不应该轻易下"绝对"的结论。


转载请注明出处:https://www.jianshu.com/p/f7cb40a8ce22

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

推荐阅读更多精彩内容