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

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

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

我们以前看到的很多架构变迁或者演进方面的文章大多都是针对架构方面的介绍,很少有针对代码级别的性能优化介绍。本文将针对一些代码细节方面的东西进行介绍,欢迎大家吐槽以及提建议。
服务器环境
  • 服务器配置: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

分享给朋友:

相关文章

AI技术领先的微软、谷歌,竟然输给了亚马逊?

AI技术领先的微软、谷歌,竟然输给了亚马逊?

[ FT12短网址资讯 ] 一家长青不败的公司,肯定不会把鸡蛋放在一个篮子,其会建立一个庞大的生态系统。对于一家AI公司来说,想要长足的发展,就需要建立庞大的生态体系,而要想建立庞大的AI生态体系,就需要同时满足应用场景、生态系统...

跳水女王陈若琳创业 放下5块金牌做起护眼灯

【亿邦动力网讯】台封王,参加三届奥运会拿了5块金牌,成功实现跳水"大满贯"的陈若琳,金牌数目更超越师姐伏明霞。去年,24岁的她发了条微博,淡然宣布退役。退役后的陈若琳需要一个新的开始新的方向。陈若琳找到了同样热爱公益的迟...

关于《摔跤吧!爸爸》,你一定没有看懂的几点

近来霸屏的《摔跤吧!父亲》,叙述了曾经的摔跤冠军辛格,培养两个女儿变成女子摔跤冠军,打破印度传统的勉励故事。形象比较深的一个细节是,两个小女子要坚持天天早上5点钟起床操练。而反观咱们,想要每周读一本书,可是迟迟都未实施;想要天天早上,但即是...

微服务架构 API 的开发与治理

微服务架构 API 的开发与治理

虽然已经红了很久,但是“微服务架构”正变得越来越重要,也将继续火下去。各个公司与技术人员都在分享微服务架构的相关知识与实践经验,但我们发现,目前网上的这些相关文章中,要么上来就是很有借鉴意义的干货,要么就是以高端的专业术语来讲...

独立书店这个“城市乌托邦”,能寄托都市里孤独的灵魂吗?

独立书店这个“城市乌托邦”,能寄托都市里孤独的灵魂吗?

[ FT2短网址 ] 缘何这些装饰典雅、综合经营的书店会以一种“逆势”的姿态在大城市中的高档商崛起?对比之前的实体书店,这些书店出现、生存和发展的支撑点在何处?图片来自“123rf.com.cn”“有了亚马逊,还有什么必要去书店?...

贾跃亭:FF 91已经启动全新生产计划

贾跃亭在其个人公众号称,2017派克峰国际爬山赛落下帷幕。FF首款量产车FF 91以11:25:083的成绩成功完赛,并以23秒的优势,打破了由特斯拉Model S保持的量产电动车型最快纪录。美国著名媒体CNN全程记录FF 91参赛实况,并...

发表评论

访客

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