在数字化浪潮汹涌的当下,信息传播和服务提供高度依赖APP、网站等线上平台,其性能与稳定性尤为关键,直接左右着用户体验和业务的顺畅推进。
在日常上线发布流程中,某核心服务在发布过程中,频繁遭遇上游接口超时的棘手难题,该问题会直接导致APP相关流程短暂异常,影响用户体验,并且系统监控警报声此起彼伏,这给我们带来极大困扰。
通过查看线上服务监控平台与服务日志,我们发现实例刚引入流量时,最初几个请求的接口响应极为迟缓,且涉及不同接口,而在这批接口响应成功后,后续接口响应则恢复正常。基于此现象,我们展开了初步排查:
1.硬件资源排查:查看服务的机器数量充足,同时利用监控平台查看 CPU 使用率、内存使用率、磁盘 I/O 等指标,确认服务的硬件资源满足高并发需求。
2.网络资源排查:借助监控平台查看网络带宽的使用情况以及网络延迟数据。与网络同事沟通,确认网络环境无异常。
3.接口性能排查:对接口的代码逻辑、算法复杂度、是否存在慢SQL进行审查,确定接口自身性能无问题。
4.内存泄露排查:借助监控平台与dump文件深入检查了堆内存大小、确认内存使用情况正常,不存在内存持续增长且无法释放的现象。
5.JVM参数排查:检查服务配置的jvm参数与服务生效的jvm参数,验证配置的参数生效的。我们借助监控平台查看GC情况,堆内存使用情况,检查垃圾回收器的参数设置等关键指标。查看服务对应机器配置情况为2C4G,初始内存设置为2G,最大堆内存设置2G, 使用G1垃圾回收器,最大垃圾回收暂停时间为200ms。JVM 参数配置合理,能确保 JVM 能够高效稳定地运行,不存在内存不足或垃圾回收频繁而引发的性能瓶颈。
具体JVM 配置参数如下:
java -jar -Dfile.encoding=UTF-8 -server -Xms2G -Xmx2G -Xss512k -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/dump ${project.build.finalName}.jar
服务jvm配置属性截图:
经过初步排查,排除了上述可能的问题,怀疑是服务的tomcat线程池初始化或数据库初始化未完成导致。
通过查看监控视图以及检查线程配置的详细数据,判断tomcat线程数是否增长正常。依据获取的数据进行分析,我们发现站点 Tomcat 初始化线程的配置,其最小值设为 100,最大值为 200。借助监控图表得以证实,Tomcat 初始化配置切实生效,且程序运行数分钟后线程数才开始增缓慢长。这一结果表明,tomcat线程池初始化并非导致接口变慢的缘由。
Tomcat线程池初始化参数配置:
java -jar -Dfile.encoding=UTF-8 -Dserver.tomcat.max-threads=200 -Dserver.tomcat.min-spare-threads=100
Tomcat线程数监控截图:
首先,检查连接池的大小、获取连接的等待时间等配置参数,同时与 DBA 同事共同确认配置的合理性。经过确认,数据库连接池配置合理。然后,我们继续排查是否是接口初始化和数据库初始化导致。为了方便排查,我们需要运用 jProflier 工具(该工具详细使用教程可网上自行学习)。
第一步:本地启动站点项目,启动 jProflier 应用,在 jProflier 中选择需要观察的应用,相关截图如下:
第二步:随机请求一个接口,在 jProflier 应用中选择 call Tree 指标,找到接口调度的指定线程,观察接口耗时详情情况。
服务日志截图:
jProflier应用截图:
第一次请求分析截图:
第二次请求分析截图:
接口总耗时 | mybatis组件初始化耗时 | 数据库创建耗时 | |
---|---|---|---|
第一次请求 | 962ms | 500ms | 300ms |
第二次请求 | 253ms | 6ms | 5ms |
@Component
public class MysqlRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MysqlRunner.class);
@Autowired
private Controller controller;
@Override
public void run(ApplicationArguments args) {
try {
logger.info("springboot启动后预热执行开始-------------" );
Long start = System.currentTimeMillis();
Request request = new Request();
//写死一个查询条件调度接口
request.setParmas(1);
request.setLimit(1);
controller.query(request);
logger.info("springboot启动后预热执行结束,耗时" + (System.currentTimeMillis() - start) + "ms-------------" );
} catch (Exception e) {
logger.error("springboot启动后预热执行异常-------------", e);
}
}
}
优化前耗时 | 优化后耗时 | |
---|---|---|
第一次请求 | 962ms | 289ms |
第二次请求(与第一次入参相同) | 253ms | 226ms |
第三次请求(与第一次入参不同) | 263ms | 231ms |
最后上线观察成果:站点发布时,上游站点再无超时告警,问题得以成功解决!
对于高并发场景下接口超时问题优化,一般解决方案有以下五步:
1.检查硬件资源:判断服务机器是否充足,是否需要扩容。
2.检查网络资源:判断网络环境是否稳定,带宽是否充足。
3.检查jvm参数:判断是否存在内存泄露,垃圾回收是否正常,jvm配置是否合理。
4.检查接口性能:判断接口本身性能问题,是否存在慢SQL,redis初始化等问题。
5.检查服务程序:是否需要初始化预热,包括且不限于tomcat线程池初始化,数据库初始化等。
以上是本次关于高并发性能优化对应解决方案,希望本篇文章能给广大开发者带来启示和切实的帮助!如果您遇到类似问题,不妨参考我们的解决思路。也欢迎大家留言分享自己的经验和看法,让我们共同在技术的海洋中不断进步!
Chen·Small-K 信也科技后端研发
往期精彩内容指路