服务发布或重启,发生抖动怎么办?
共 2807字,需浏览 6分钟
·
2021-07-09 05:09
点击关注公众号,Java干货及时送达
作者:zhanlijun
来源:http://www.cnblogs.com/LBSer
一、问题描述
在发布或重启某线上某服务时(jetty8作为服务器),常常发现有些机器的load会飙到非常高(高达70),并持续较长一段时间(5分钟)后回落(图1),与此同时响应时间曲线(图2)也与load曲线一致。
注:load飙高的初始时刻是应用服务端口打开,流量打入时:
二、问题排查方法
发布时对资源使用情况进行监控。
1)通过top -H -p 查找cpu使用率较高的线程,发现2129和2130这两个线程cpu使用较高。
2)通过jstack打印栈信息,并将线程号2129和2130转换成16进制(printf "%x\n" 2129),分别为851和852,发现这两个线程是编译线程(表1)。
此外当这两个线程cpu使用率降低后load以及响应时间也马上恢复了正常,时间点非常吻合。
"C2 CompilerThread1" daemon prio=10 tid=0x00007fce48125800 nid=0x852 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" daemon prio=10 tid=0x00007fce48123000 nid=0x851 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
三、现象解释
C2 CompilerThread线程项目启动初期cpu使用率那么高,它在干什么呢?
四、解决思路
1)预热
如果在服务接受线上请求之前提前完成编译优化过程,那么将能避免此种抖动情况。
一般的做法是预热,有两种方法:
a)程序主动预热:在启动完成后,程序主动的访问热点的代码,确保主要的热点代码已被编译成机器码后再放入流量,可通过-XX:+PrintCompilation来确认。
b)复制流量预热:通过tcpcopy软件拷贝一份线上nginx的流量进行预热,完成之后再导入线上流量。
2)启动多个线程进行编译优化
如果能加快编译优化速度,那也能降低解释执行阶段导致的抖动时间。因此可以多拿几个线程来做编译,加快达到高峰性能的速度。另外,多线程系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。
可以使用-XX:CICompilerCount参数来设置编译线程数目,这个值默认是2(之前在栈里看到有两个编译线程),我们可以加到4。
3)采用多层编译
编译方式有三种:
1)Client模式;
2)Server模式;
3)Tiered模式。
我们服务默认是Server模式。Server模式是采用c2高级编译的,会比较耗时且要运行一段时间才会触发编译。Server模式的优点是编译后程序效率较高;
Client模式比较轻量也比较快触发(比Server模式触发快),编译优化后程序效率不如Server模式;
Tiered模式是Client模式和Server模式的折中,一开始会启用Client模式,可以在启动后更快的让部分代码先进入编译优化阶段,之后会启动Server模式,达到程序效率最大优化的目的。
Oracle JDK 7里的HotSpot VM已经开始有比较好的Tiered编译(tiered compilation)支持,可以设置参数-XX:+TieredCompilation来启动Tiered模式,java 8默认就是Tiered模式。《 46 张 PPT 弄懂J VM 调优》推荐看下。
图4是到截取的不同编译方式的性能比较图,横坐标是时间,纵坐标是性能。可以看出Tired模式开始阶段性能与C1相当,当到达某一时刻后性能与C2相当。
五、结果分析
简单起见采用方案2和方案3来进行优化。
采用方案2和3之后进行了多次发布,发布时除个别机器load达到10之外,基本没有过高现象(在2~4范围内),并且短时间(2分钟)内,load都会降到较合理水平(2左右),较发布时的load来看,比优化前要好很多。
方案2和方案3只是降低了抖动持续的时间以及抖动强度,并不能完全避免抖动。真正能避免抖动的方案应该是方案1,通过预热的方式实现平滑发布或重启。最后,关注公众号Java技术栈,在后台回复:面试,可以获取我整理的 Java 系列面试题和答案,非常齐全。
关注Java技术栈看更多干货