对于线上系统调优,它本身是个技术活,不仅需要很强的技术实战能力,很强的问题定位,问题识别,问题排查能力,还需要很丰富的调优能力。
图片来自 Pexels
本篇文章站在实战角度,从问题识别,问题定位,问题分析,提出解决方案,实施解决方案,监控调优后的解决方案和调优后的观察等角度来与大家交流分享线上高并发调优整个闭环过程。
项目简要情况概述
该项目为基于 SSM 架构的商城类单体架构项目,其中有一个秒杀重磅模块,如下为当前线上环境的简要架构部署图。
大致描述一下:
项目为 SSM 架构。
服务器类别:1 台负载均衡服务器(F5),3 台运用程序服务器,1 台计时器服务器,1 台 Redis 服务器,1 台图片服服务器和 1 台基于 Pass 架构的 MySQL 主从服务器(微软云)。
调用逻辑:上图为简要调用逻辑。
何为单体架构项目
从架构发展角度,软件项目经历了如下阶段的发展:
单体架构:可理解为传统的前后端未分离的架构。
垂直架构:可理解为前后端分离架构。
SOA 架构:可理解为按服务类别,业务流量,服务间依赖关系等服务化的架构,如以前的单体架构 ERP 项目,划分为订单服务,采购服务,物料服务和销售服务等。
微服务:可理解为一个个小型的项目,如之前的 ERP 大型项目,划分为订单服务(订单项目),采购服务(采购项目),物料服务(物料项目)和销售服务(销售项目),以及服务之间调用。
本 SSM 项目引发的线上问题
①当秒杀的时候,CPU 暴增
该系统每天秒杀分为三个时间端:10 点,13 点和 20 点,如下为秒杀的简要页面:
②单台运用服务器 CPU
③单台运用服务器请求数
④rdis 连接数(info clients)
这个未保存截图,记得是 600 左右:
connected_clients:600
⑤MySQL 请求截图
排查过程及分析
排查思路
根据服务部署和项目架构,从如下几个方面排查:
运用服务器:排查内存,CPU,请求数等。
文件图片服务器:排查内存,CPU,请求数等。
计时器服务器:排查内存,CPU,请求数等。
Redis 服务器:排查内存,CPU,连接数等。
DB 服务器:排查内存,CPU,连接数等。
排查过程
在秒杀后 30 分钟内:
①运用程序服务器 CPU 暴增,内存暴增,造成 CPU 和内存暴增的根本原因是请求数过高,单台运用服务器达到 3000 多。
②Redis 请求超时,如下图:
③JDBC 连接超时,如下图:
④通过 GC 查看,发现 24 小时内,FullGC 发生了 152 次,如下图:
⑤再看看堆栈,发现有一些线程阻塞和死锁。
jstat -l pid,也可以通过 VisualVM 分析:
⑥发现有 2000 多个线程请求无效资源,如下图:
造成本次系统异常主要因素分析
造成本次系统异常主要因素分析如下:
在秒杀时,请求量过高,导致运用服务器负载过高。
Redis 连接池满,获取不到连接,connot get a connection from thread pool。
JDBC 连接池满,获取不到连接和超时。
存在大对象代码,如向 List 集合中不停添加对象,不能及时回收对象导致内存增加,频繁发生 Full GC。
Tomcat 并发参数,JVM 优化参数,Jedis 配置参数,JDBC 配置参数不合理。
未对请求量进行削峰和限流。
资源连接未及时释放,如 Redis 连接,JDBC 连接未及时释放。
最终解决方案
①增加运用服务,做流量削峰和分流
由于该项目未增加 MQ,因此只能采用硬负载,增加服务器水平扩展方式来实现流量削峰和流量分流:
②优化 JVM 参数,如下为本次优化后的参数:
JAVA_OPTS="-server -Xmx9g -Xms9g -Xmn3g -Xss500k -XX:+DisableExplicitGC -XX:MetaspaceSize=2048m -XX:MaxMetaspaceSize=2048m -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -Dfile.encoding=UTF8 -Duser.timezone=GMT+08"
关于这个 JVM 参数的优化,JVM 理论是怎样的,官方建议是怎样的,实战是怎样的,将在下篇文章中分析。
③优化 Tomcat 并发相关参数
主要是两方面:
修改 bio 协议为 nio2 。
根据服务器配置,业务场景,业务流量等合理设置相关参数,尽量达到最优。
关于 Tomcat 相关参数优化,在接下来的文章中分析。
④Redis 和 JDBC 参数优化
由于涉及到安全性问题,这里不列出。
⑤代码优化
代码优化如下:
优化掉大对象。
优化未及时释放的对象和连接资源。
⑥解决 000 多个线程请求无效资源问题:
在conf/context.xml增大缓存 <Resource cachingAllowed = "true" cacheMaxSize = "102400" />
最终优化结果
经过几天观察,系统平稳。
基本监控,如下图:
GC,如下图:
抽样器 CPU 和内存:
总结
由于篇幅的限制有些细节和优化手段未在本篇文章中提及。虽然解决了该问题,但是从长远来看,该单体项目任然存在很大的问题和隐患。
下面随便举几个:
前后端紧耦合,未分离。
由于该系统秒杀业务属于非持续性并发,即局部性并发,当前并未做局部并发架构的调整。
由于该系统秒杀业务与该项目紧紧耦合在一起,未进行隔离,未独立成单独模块,未单独部署,从而存在因秒杀业务造成整个系统瘫痪的风险。
未做流量削峰和流量限流,如加 MQ 等软手段。
Redis 为做高可用集群。
作者:Alan_beijing
编辑:陶家龙
出处:http://www.cnblogs.com/wangjiming/