当前位置:首页 > 短网址资讯 > 正文内容

从代码层面优化系统性能应该怎么做?

www.ft12.com7年前 (2017-08-16)短网址资讯1916

我们以前看到的很多架构变迁或者演进方面的文章大多都是针对架构方面的介绍,很少有针对代码级别的性能优化介绍。本文将针对一些代码细节方面的东西进行介绍,欢迎大家吐槽以及提建议。
服务器环境
  • 服务器配置:4 核 CPU,8G 内存,共 4 台

  • MQ:RabbitMQ

  • 数据库:DB2

  • SOA 框架:公司内部封装的 Dubbo

  • 缓存框架:Redis、Memcached

  • 统一配置管理系统:公司内部开发的系统

问题描述
  1. 单台 40TPS,加到 4 台服务器能到 60TPS,扩展性几乎没有。

  2. 在实际生产环境中,经常出现数据库死锁导致整个服务中断不可用。

  3. 数据库事务乱用,导致事务占用时间太长。

  4. 在实际生产环境中,服务器经常出现内存溢出和 CPU 时间被占满。

  5. 程序开发的过程中,考虑不全面,容错很差,经常因为一个小 bug 而导致服务不可用。

  6. 程序中没有打印关键日志,或者打印了日志,信息却是无用信息没有任何参考价值。

  7. 配置信息和变动不大的信息依然会从数据库中频繁读取,导致数据库 IO 很大。

  8. 项目拆分不彻底,一个 Tomcat 中会布署多个项目 WAR 包。

  9. 因为基础平台的 bug,或者功能缺陷导致程序可用性降低。

  10. 程序接口中没有限流策略,导致很多 VIP 商户直接拿我们的生产环境进行压测,直接影响真正的服务可用性。

  11. 没有故障降级策略,项目出了问题后解决的时间较长,或者直接粗暴的回滚项目,但是不一定能解决问题。

  12. 没有合适的监控系统,不能准实时或者提前发现项目瓶颈。

优化解决方案
1、数据库死锁优化解决

我们从第二条开始分析,先看一个基本例子展示数据库死锁的发生:

注:在上述事例中,会话 B 会抛出死锁异常,死锁的原因就是 A 和 B 二个会话互相等待。

分析:出现这种问题就是我们在项目中混杂了大量的事务 +for update 语句,针对数据库锁来说有下面三种基本锁:

  • Record Lock:单个行记录上的锁

  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身

  • Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身

当 for update 语句和 gap lock 和 next-key lock 锁相混合使用,又没有注意用法的时候,就非常容易出现死锁的情况。

那我们用大量的锁的目的是什么,经过业务分析发现,其实就是为了防重,同一时刻有可能会有多笔支付单发到相应系统中,而防重措施是通过在某条记录上加锁的方式来进行。

针对以上问题完全没有必要使用悲观锁的方式来进行防重,不仅对数据库本身造成极大的压力,同时也会把对于项目扩展性来说也是很大的扩展瓶颈,我们采用了三种方法来解决以上问题:

  • 使用 Redis 来做分布式锁,Redis 采用多个来进行分片,其中一个 Redis 挂了也没关系,重新争抢就可以了。

  • 使用主键防重方法,在方法的入口处使用防重表,能够拦截所有重复的订单,当重复插入时数据库会报一个重复错,程序直接返回。

  • 使用版本号的机制来防重。

以上三种方式都必须要有过期时间,当锁定某一资源超时的时候,能够释放资源让竞争重新开始。

2、数据库事务占用时间过长

伪代码示例:

项目中类似这样的程序有很多,经常把类似 httpClient,或者有可能会造成长时间超时的操作混在事务代码中,不仅会造成事务执行时间超长,而且也会严重降低并发能力。

那么我们在用事务的时候,遵循的原则是快进快出,事务代码要尽量小。针对以上伪代码,我们要用 httpClient 这一行拆分出来,避免同事务性的代码混在一起,这不是一个好习惯。

3、CPU 时间被占满分析

下面以我之前分析的一个案例作为问题的起始点,首先看下面的图:

项目在压测的过程中,CPU 一直居高不下,那么通过分析得出如下分析:

数据库连接池影响

我们针对线上的环境进行模拟,尽量真实的在测试环境中再现,采用数据库连接池为咱们默认的 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);

修改完成以后,并发量重新上升到 100 以上 TPS,但是当并发量非常大的时候,项目 GC(垃圾回收能力下降),分析原因还是因为 Executors.newFixedThreadPool(50) 这一行,虽然解决了产生无限线程的问题,但是当并发量非常大的时候,采用 newFixedThreadPool 这种方式,会造成大量对象堆积到队列中无法及时消费,看源码如下:

可以看到采用的是无界队列,也就是说队列是可以无限的存放可执行的线程,造成大量对象无法释放和回收。

最终线程池技术方案

方案一

注:因为服务器的 CPU 只有 4 核,有的服务器甚至只有 2 核,所以在应用程序中大量使用线程的话,反而会造成性能影响,针对这样的问题,我们将所有异步任务全部拆出应用项目,以任务的方式发送到专门的任务处理器处理,处理完成回调应用程序器。后端定时任务会定时扫描任务表,定时将超时未处理的异步任务再次发送到任务处理器进行处理。

方案二

使用 AKKA 技术框架,下面是我以前写的一个简单的压测情况:

http://www.jianshu.com/p/6d62256e3327

4、日志打印问题

先看下面这段日志打印程序:

像这样的代码是严格不符合规范的,虽然每个公司都有自己的打印要求。

首先日志的打印必须是以 logger.error 或者 logger.warn 的方式打印出来。

日志打印格式:[系统来源] 错误描述 [关键信息],日志信息要能打印出能看懂的信息,有前因和后果。甚至有些方法的入参和出参也要考虑打印出来。

在输入错误信息的时候,Exception 不要以 e.getMessage 的方式打印出来。

合理的日志格式是:

我们在程序中大量的打印日志,虽然能够打印很多有用信息帮助我们排查问题,但是更多是日志量太多不仅影响磁盘 IO,更多会造成线程阻塞对程序的性能造成较大影响。

在使用 Log4j1.2.14 版本的时候,使用如下格式:

%d %-5p %c:%L [%t] - %m%n

那么在压测的时候会出现下面大量的线程阻塞,如下图:

再看压测图如下:

原因可以根据 log4j 源码分析如下:

注:Log4j 源码里用了 synchronized 锁,然后又通过打印堆栈来获取行号,在高并发下可能就会出现上面的情况。

于是修改 Log4j 配置文件为:

%d %-5p %c [%t] - %m%n

上面问题解决,线程阻塞的情况很少出现,极大的提高了程序的并发能力,如下图所示:

扫描二维码推送至手机访问。

版权声明:本文由短链接发布,如需转载请注明出处。

本文链接:https://www.ft12.com/article_414.html

分享给朋友:

相关文章

喂不饱的特斯拉,恐怕最后还得携手AMD自研芯片

喂不饱的特斯拉,恐怕最后还得携手AMD自研芯片

[ FT12短网址 ] 据外媒报道,AMD的半导体代工厂GlobalFoundries的CEO Sanjay Jha在该公司的技术大会上确认,他们正在与特斯拉合作生产用于自动驾驶程序的定制化芯片,并为后者提供晶元生产代工服务。图片...

共享汽车为什么跑不起来:资本力挺,现实阻碍

共享汽车为什么跑不起来:资本力挺,现实阻碍

[ FT12短网址 ] 如今,牌照、停车位、市场渠道都是共享汽车发展的障碍,而面对无法快速规模化、近乎千篇一律的竞争模式,谁也难以脱颖而出。图片来自网络9月15日,Gofun出行发布消息,其共享汽车网点将由300个固定网点增加到上...

巨头纷纷布局,长租公寓前景并非一片光明

巨头纷纷布局,长租公寓前景并非一片光明

[ FT12短网址 ] 不管是在物业和融资上独占鳌头的品牌开发商,还是擅长轻巧灵活运营的创业公司,“赔本赚吆喝”的买卖在长租公寓掘进的进程中不可能长久持续,虽然长久下去肯定是赚钱的,但是一旦摊子铺开,铺得太大,亏损无疑。图片来自“...

“色流”产业十年风云录:40万大军,吸食百亿利润

色流群体如蚂蚁一般,跟随着流量蜜罐迁移,经历多个时代的轮回变迁,并分食百亿级别市场……标题:“色流”产业十年风云录:40万大军,吸食百亿利润来源:一本财经,已授权『互联网的一些事』,转载请联系作者。在“流量为王”的时代,流量在某种意义上,就...

FT12短网址:苏宁和国美同时抢滩汽车市场 车源或为成败要素

2017年7月15日,苏宁易购轿车超市(以下简称“轿车超市”)在南京中心商圈新街口开业,产品定位中高端,将O2O形式应用到轿车消费商场。苏宁方面表明,将来将在全国开设超越100家轿车超市。无独有偶,3月28日,国美电器控股在京举行轿车招商发...

FT12短网址的API分享和简介

FT12短网址的API分享和简介

2008年初,FT12短网址发布了在线版链接生成器ft12.com。Google声称:"......(这是)互联网上最安稳、最安全、最快速的短网址生成器。"有人做了比较,证实确实如此。从上图能够看到,ft12.com的响...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。