我们以前看到的很多架构变迁或者演进方面的文章大多都是针对架构方面的介绍,很少有针对代码级别的性能优化介绍。本文将针对一些代码细节方面的东西进行介绍,欢迎大家吐槽以及提建议。服务器环境
- 服务器配置:4 核 CPU,8G 内存,共 4 台
- MQ:RabbitMQ
- 数据库:DB2
- SOA 框架:公司内部封装的 Dubbo
- 缓存框架:Redis、Memcached
- 统一配置管理系统:公司内部开发的系统
- 系统开发谢天华 135-0148-8501
问题描述
- 单台 40TPS,加到 4 台服务器能到 60TPS,扩展性几乎没有。
- 在实际生产环境中,经常出现数据库死锁导致整个服务中断不可用。
- 数据库事务乱用,导致事务占用时间太长。
- 在实际生产环境中,服务器经常出现内存溢出和 CPU 时间被占满。
- 程序开发的过程中,考虑不全面,容错很差,经常因为一个小 bug 而导致服务不可用。
- 程序中没有打印关键日志,或者打印了日志,信息却是无用信息没有任何参考价值。
- 配置信息和变动不大的信息依然会从数据库中频繁读取,导致数据库 IO 很大。
- 项目拆分不彻底,一个 Tomcat 中会布署多个项目 WAR 包。
- 因为基础平台的 bug,或者功能缺陷导致程序可用性降低。
- 程序接口中没有限流策略,导致很多 VIP 商户直接拿我们的生产环境进行压测,直接影响真正的服务可用性。
- 没有故障降级策略,项目出了问题后解决的时间较长,或者直接粗暴的回滚项目,但是不一定能解决问题。
- 我们针对线上的环境进行模拟,尽量真实的在测试环境中再现,采用数据库连接池为咱们默认的 C3P0。
那么当压测到二万批,100 个用户同时访问的时候,并发量突然降为零!报错如下:
com.yeepay.g3.utils.common.exception.YeepayRuntimeException: Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
那么针对以上错误跟踪 C3P0 源码,以及在网上搜索资料发现 C3P0 在大并发下表现的性能不佳。
线程池使用不当引起
以上代码的场景是每一次并发请求过来,都会创建一个线程,将 DUMP 日志导出进行分析发现,项目中启动了一万多个线程,而且每个线程都极为忙碌,彻底将资源耗尽。
那么问题到底在哪里呢???就在这一行!
private static final ExecutorService executorService = Executors.newCachedThreadPool();
在并发的情况下,无限制的申请线程资源造成性能严重下降,在图表中显抛物线形状的元凶就是它!!!那么采用这种方式最大可以产生多少个线程呢??答案是:Integer 的最大值!看如下源码:
那么尝试修改成如下代码:
private static final ExecutorService executorService = Executors.newFixedThreadPool(50);