Spring boot性能优化

      笔者刚入职新公司领导让针对api项目进行重构,由于当前系统用play框架写的加上历史遗留原因,造成当前的api项目难以维护以及部署。重构便成了迫在眉睫的事。由于公司的业务性质,要求单台机器api的吞吐量很高,大家都知道springboot的好处,可以快速搭建起web服务。所以在选型时笔者只是写了个简单的接口然后用ab命令对这个接口进行了性能压测。因为笔者认为吞吐量问题springboot可以完全胜任。没有过多的考虑性能不达标的问题。

      于是笔者便开开心心的按照老系统的逻辑进行重构。根据需求接口返回类型需要根据请求后缀是json还是xml提供相应的返回数据格式。其他后缀结尾的或者没有后缀的返回错误码。笔者当时想到两种方案。一种是直接在@RequestMapping注解中通过value设置支持的后缀格式。如:@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = RequestMethod.GET)。另一种是在@RequestMapping中不设置后缀如图一。通过实现WebMvcConfigurer配置类。实现configurePathMatch方法开启后缀匹配。实现configureContentNegotiation方法根据后缀进行返回格式设置如图二。然后再写个拦截器对非json和xml结尾的请求进行拦截如图三。为了简单少写代码。笔者选择了第二种方式实现。然后就开启了撸代码的模式。在完成所有开发任务,进入测试阶段时。测试小朋友跑过来跟我说:少年你重构的api性能不达标。现有的2核4G单机QPS能达到2000。你重构的只能达到七八百。当时内心数万个草泥马在奔腾。

图一
图二
图三

      没办法各种百度寻找优化方案。试过换各种web容器。由tomcat换到jetty再到undertow。试过配置各种参数。然而并没有什么提升。看到一篇文章说可以使用异步请求如图四。先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成时再对客户端进行响应。顿时喜出望外,以为找到了解决的办法。然而并没有什么卵用。一度怀疑最初的选型是错误的。但是我想springboot的性能应该不能这么不堪吧。于是便开始查找自己的代码。跟踪线程耗时方法。

图四

      有过性能调优的同学应该都熟悉 jvisualvm,jdk自带监控程序。可以监控本地或远端cpu、内存、线程等实时动态信息。以及对线程进行快照。对线程内方法调用耗时统计等功能。非常强大。笔者用的是undertow做为web容器。可以看到图五、图六它有跟netty类似的IO模型,IO线程负责接收请求,然后把请求放到任务池中,由后面的任务线程进行处理。这也解释了为什么我之前用异步请求没有提升性能的原因。因为本身undertow已经是异步的了。自己再进行异步操作毫无意义。tomcat也是同样的道理。tomcat7以上默认支持NIO,所以自己再实现异步请求操作没有什么意义。

图五
图六

    然后我用wrk命令进行压测,看下任务线程中哪些操作是比较耗时的,wrk -t 10 -c 500 -d 15s --latency -s http://127.0.0.1:2551/ping.json。10个线程500个连接,持续15秒。可以看到没有任何业务逻辑的接口QPS只有1715。对任务线程抽样进行快照如图八。展开其中一个线程任务图九。查看耗时的调用方法。如图十中DispatcherServlet在调用doDispatch方法占用了64.2%的时间。一个doDispatch怎么会用这么多的时间呢?继续追踪方法内调用getHander,最后耗时在getMatchingCondition中。

图七
图八
图九
图十

查看源码从doDispatch开始跟踪,发现当程序启动时会把@RequestMapping注解的path放到map集合中,当有请求时,先去map中获取对应的路径,如果有则返回方法,没有则根据设置的后缀匹配规则进行遍历匹配图十三。其中画框的属性是不是很熟悉。对,它就是实现WebMvcConfigurer时设置的配置。  如写的是@RequestMapping(value = {"/ping"}, method = {RequestMethod.GET}) ,但请求的是/ping.json,第一次查找在集合中没有以/ping.json为path的方法,就会遍历所有路径集合进行拆分后缀匹配。直到匹配到为止。笔者的项目中有300个接口,500多个路径。如果不显示的给出后缀,每次请求都会遍历一遍这500多个路径,造成耗时。

图十一
图十二
图十三

      最后猜想是匹配路径耗时导致吞吐量变低。于是把注解中路径后缀显示给出@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = {RequestMethod.GET}) , 再进行一次压测。结果QPS为9384,翻了4倍多。到此为止才算把性能提升上来。符合上线标准。

图十四

      此次调优过程中发现还有好多需要优化的地方,比如日志,集成的swagger,actuator等等。都多少影响性能。但为了增加必要功能,损失些性能也是可以接受的,有些不必要的损失性能还是要找到根源解决掉,笔者遇到的情况未必适合所有人。不过可以给那些想提升性能的朋友提供一些思路。

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