通过压测发现整个服务下所有接口的TPS都不很高的样子,在服务器被很多个线程同时访问时cpu占用率都不很高。所以初步判定可能是web服务器的配置问题,导致服务器的性能没被充分使用。下面开始进行优化。
刚开始的优化方案是准备直接进行tomcat的调优一把梭。临时兴起看了一下pom依赖。pom部分代码如下:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-undertow
可以比较清晰的看出当前springboot排除了tomcat,引用了undertow作为web服务器。
接下来是查阅资料,对三种服务器进行对比,此处重点分析tomcat和undertow两个服务器:
此处对三个常用web服务器的response的headers进行对比:
1.Tomcat Response Headers
Content-Type →application/json;charset=UTF-8
Date →Mon, 09 Jan 2017 02:23:26 GMT
Transfer-Encoding →chunked
X-Application-Context →JcgSpringBootContainers:# Application index.
2 Jetty Response Headers
Content-Type →application/json;charset=UTF-8
Date →Mon, 09 Jan 2017 02:29:21 GMT
Transfer-Encoding →chunked
X-Application-Context →JcgSpringBootContainers:# Application index.
3 Undertow Response Headers
Connection →keep-alive
Content-Type →application/json;charset=UTF-8
Date →Mon, 09 Jan 2017 02:20:25 GMT
Transfer-Encoding →chunked
X-Application-Context →JcgSpringBootContainers:# Application index.
关于undertow的基础实现参考文章:
https://www.iteye.com/blog/wildfly-2040380
https://www.iteye.com/blog/wildfly-2040377
下面是对性能的深入分析:
援引两位腾讯云的测试大佬的测试结果:
https://cloud.tencent.com/developer/article/1644339
“在简单接口中,发现tomcat
对于cpu
的占用是最低的,而且吞吐量最高。另外jetty
停止测试花费了最长的时间导致吞吐量变低。
简单接口下吞吐量:tomcat
>jetty
>undertow
。
cpu占用率:tomcat
>jetty
>undertow
。
在稍复杂的接口测试下,tomcat
和underwow
的差距并没有很大,但是tomcat
在吞吐量和cpu方面依然占据了稍小的优势,jetty
与前两者存在差距。
复杂接口下吞吐量:tomcat
>undertow
>jetty
。
cpu:tomcat
>undertow
>jetty
。
从结果来看,tomcat
依然是最稳定的服务器,而不是网上各种博主说的undertow
更优。而jetty
更适合长连接的服务,但是长连接我更倾向于选择netty
。undertow
更适合用于IO密集型服务器,或者文件服务器使用undertow
会是一个不错的选择,但是在一般情况下还是老老实实的使用Springboot默认的tomcat
吧。”
https://cloud.tencent.com/developer/article/1376555
“ HTTP异步的目的在帮助dispatcherservlet分担压力,提升吞吐量。但如果运行在NIO模式的服务容器上,就会产生负面影响,因为NIO本身就做了类似的事情,此时再加HTTP异步,则相当于又加了N多不必要的线程,导致性能主要消耗在线程的开销上,所以建议使用tomcat作为内嵌容器并且没有开启tomcat的NIO模式时,可以配合HTTP异步来提升程序性能。尤其是当业务繁重时,提升效果尤其明显。”
下面是undertow配置和一个针对配置的简单测试:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
# 不要设置过大,如果过大,启动项目会报错:打开文件数过多
#server.undertow.io-threads=2
# 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
#server.undertow.worker-threads=16
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
server.undertow.buffer-size=1024
# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region
server.undertow.buffers-per-region=1024
# 是否分配的直接内存(NIO直接分配的堆外内存)
server.undertow.direct-buffers=true
稍微查看一下springboot源码,io-threads是在服务器核数和2之间取最大值,worker-threads一般是io-threads的8倍。
在实测中,2核4G的机器在配置io-threads和worker-threads分别设置2,16的时候,可以启动,但系统cpu占用率并不高,单核占用60%,在分别设置2,400的时候cpu占用率几乎100%,服务器还是可以正常运行。io-threads优化空间不高,worker-threads的最佳配置还需要进一步研究。