当前位置: 首页 > 后端技术 > Java

3 步排查,3 步优化,探针性能损耗直降 44%

时间:2023-04-02 10:28:44 Java

三步调查,三步优化,探针性能损失下降44%针性能损失。下表是用户200个并发压测下两个核心链路的性能数据对比。可以看出,连接探头后性能损耗居高不下。3-stepquickcheck1.比较链路差异首先想到的检查方案是通过skywalking监控来检查,比较应用在接入probe和不接入probe的情况下,性能有什么区别,以及具体表现是消费了哪个中间件。对比skywalking监控的链路耗时,确实可以观察到非接入探头的RT高于接入探头和链路,但不清楚是客户环境有问题还是skywallking上的链接已损坏。信息不全面,无法准确定位。2、插件排除法当没有其他有效信息时,尝试通过排除法定位具体影响性能的插件。具体做法是先梳理一下链路中用到的中间件,先去掉所有的中间件插件,然后逐一添加个别的中间件插件,不断进行压测,观察哪个插件的影响更大性能上。依靠这个方法,成功定位了dubbo和logback这两个插件,它们对性能的影响还是比较大的。3.性能数据采集在压测的同时,我们在agent框架中加入了中间件插件拦截器方法执行的耗时统计代码,这部分数据会统一输出到一个固定日志文件。另外,我们开发了配套的性能日志分析程序,可以通过日志采集脚本分析整个链路所有应用打印的性能数据,并输出汇总一个中间件拦截器的统计结果。这个结果可以可视化可以看到各个中间件的性能和耗时比。统计结果示例:性能采集代码展示:具体优化的3个步骤1.减少切点代理性能损失的最终原因是代理增强中间件代码后,会修改目标类的字节码和植入一些额外的逻辑,正是这些额外的逻辑带来了额外的耗时。切点越多,植入的逻辑越多,可能整个链路的损耗就越高,所以尽可能减少切点的数量,肯定会降低性能损耗。按照这个思路,我们重新设计了发现比较耗时的logback中间件,花了很多时间。原来的logback实现有3个分界点,分别负责:shadowappender注册、流量识别、日志隔离。重新设计后,优化为一个分界点,实现所有功能。2.静默&业务流量过滤虽然中间件插件的增强逻辑不同,但是代理植入目标类的字节码是统一的。它们都有一个统一的入口,预运行逻辑是同一套框架。实际运行时序图如下:其实Interceptor还有很多增强逻辑只在压测流量时才执行,但是这部分拦截器的执行需要经过之前Messager到Messager的一系列调用AdviceListener是完全没有必要的,会造成一定的性能损失。这种无用的调用可以在前端进行判断,从一开始就过滤掉,在达到效果的同时减少性能损失。为此,我们修改了框架,让这样的拦截器可以在前端过滤掉流量,避免执行无意义的逻辑。同时在前端增加了静音开关。静默开关可以一键禁止所有中间件增强逻辑的执行,一定程度上可以替代卸载操作。与卸载相比,它不会还原实际的字节码,也不会回收内存占用,但更轻量,响应更快,影响更小。修改后的运行时序图:3.中断逻辑优化logback插件的重新设计产生了一定的效果,但是从性能收集到的数据来看,性能损失比例最高的依然是logback插件。经过反复考虑,发现logback本身没有进一步优化的余地,于是将目光转向了框架层面,最终重点优化了CutoffInterceptor类型的中断机制。CutoffInterceptor是一个类似挡板的Interceptor,可以中断源码本身的运行,支持返回值的替换。比如实现数据库隔离,我们通常会实现一个CutoffInterceptor,在压测流量通过时返回影子数据库的连接,而不是业务连接,从而实现数据隔离。logback还实现了一个CutoffInterceptor,在压测流量通过时返回shadowappender代替业务appender实现日志隔离。CutoffInterceptor的内部实现原理是通过异常机制实现的。在替换返回值的时候,实际上是抛出一个异常,并被上层捕获,打断源码。众所周知,在java中通过抛出实现流程控制的效率其实是比较低的。我们的内测验证也证实了这一点。一个空逻辑CutoffInterceptor和同一个空逻辑Interceptor的性能差距是几十倍、几百倍。恰好logback等日志类中间件的执行频率非常高,所以这方面的性能损失并没有减少。所以我们优化了CutoffInterceptor的中断机制,把抛异常改为先通过advice设置中断标志,然后让上层判断来控制中断。最终结果经过一系列的优化动作,两条核心链路的性能损失有了很大的改善,链路A的性能损失从48%降低到4%,链路B的性能损失从35%至3.4%。