在项目开发中,有一处的需求是,定时触发一个同步逻辑,同步逻辑中是请求其他平台的接口返回的信息,与我们服务端库里的信息做比对,以拉取到的信息为基准,如果本地库里信息多则删除,少则插入,错则更正。
原本的实现方式是使用Quartz定时任务,任务执行时,虽然微服务有多个实例,执行该定时任务的只是其中一个实例,并未充分利用多实例的优势。决定对其进行优化,改为使用消息队列,在另一个微服务A中发送消息,在微服务B中消费消息,这时B服务的多个实例都会参与消费,应该可以达到一定的优化目的。
微服务A的cpu过高
经过改造后,在微服务A中新增一个定时任务,该定时任务负责通过kafka发送需要同步的信息至微服务B,B消费消息进行同步逻辑。在代码修改好后,经过测试,发现微服务A和微服务B的cpu都过高,首先排查微服务A的cpu过高问题。
此次排查使用火焰图工具Async-profiler
简单介绍下火焰图:
火焰图是基于 stack 信息生成的 SVG 图片, 用来展示 CPU 的调用栈。
y 轴表示调用栈, 每一层都是一个函数. 调用栈越深, 火焰就越高, 顶部就是正在执行的函数, 下方都是它的父函数.
x 轴表示抽样数, 如果一个函数在 x 轴占据的宽度越宽, 就表示它被抽到的次数多, 即执行的时间长. 注意, x 轴不代表时间, 而是所有的调用栈合并后, 按字母顺序排列的
火焰图是 SVG 图片, 用浏览器打开可以与用户互动。
官方地址:
https://github.com/jvm-profiling-tools/async-profiler
Async-profiler工具的使用
-
解压工具包到需要排查问题的机器上,里面有一个执行脚本profiler.sh
-
执行如下命令:
./profiler.sh -d { 采样时间s} { PID} -f { 输出文件名称}
例如,需要抓包的JAVA应用服务进程ID是XX,要抓包的时间是20s,命令如下:
./profiler.sh -d 20 XX -f /tmp/pic.svg
- 执行后,20s后会生成一个pic.svg的图片(火焰图),用浏览器打开分析。
抓到的火焰图,搜索包名,可以看到占用cpu比较高的语句如下图所示:
问题原因
可以看到cpu均被一个名字叫XXServiceImpl.getXX的内部接口占用了。通过