一次线上性能优化的过程。

A person's character isn't determined by how he or she enjoys victory but rather how he or she endures defeat.

 

今天半夜手机上收到了一条报警短信,

某一台线上TASK机器CPU负载过高,我当时在想是不是又有同事在上面新部署了半夜凌晨的task,同时跑了太多任务,导致CPU负载太高,紧接着立马登上这台服务器,键入top先定位到具体的进程:

紧接着ps -ef | grep 500558

发现是tagtask CPU占用太多导致,那么CPU占用太高主要可能有两个方面:

1.任务计算量太大

2.full gc次数频繁

首先看了下这个工程的代码,并没有发现有什么计算量,复杂度特别高的地方。

紧接着通过 jstat -gcutil | grep 500558 500 30 查看了下gc情况发现full gc 次数非常的频繁,老年代回收效率非常的低:

再通过jmap -histo:live 500558 | more查看内存中主要有哪些对象占用了大量内存,这个步骤当时没有保存截图。

接下来还是通过 jmap -dump:live, format=b,file=tagtask.hprof 500558,将dump文件下载到本地,通过IBM HeapAnalyzer 这个工具查看存活对象的引用信息:

官网下载工具,然后通过java命令启动程序,打开下载下来的dump文件:

,发现有大量的char、4个Object类型的list占用了绝大部分的jvm内存。

根据引用树,查看代码,排查问题:

1.task中主要逻辑是通过一次性读取hive表数据,发送kafka消息,这个步骤中,由于读取速度远大于producer的生成速度,导致大量的消息堆积在本地没有发送出去。

2.有4个队列,每次先从hive中读取所有数据再内存里,然后再发送消息,这样就容易导致占用大量jvm内存无法释放:

问题查出来了,那么接下来就需要我们想办法去优化这个task来解决这个事情:

思路:

现在是通过一个while循环,全量拿到hive数据,再通过这个list来处理数据,那么可不可以将list调整成一个阻塞队列,while里一边存,处理的线程一边消费呢?

这样的好处时,内存不会堆积,并且从阻塞队列里拿出数据处理,该对象会很快就会被垃圾回收掉。不会一直占有大量内存。

 

改进之后的代码如下:

首先定义好一些公共变量,一个线程池,用来管理读取hive子线程,2个map一个存储的是生成结束的标识,一个存储的是不同业务的处理队列。countdownlatch是其他的优化点,不用太关注哈。

取HIVE数据这一块:

在拿到resultRet后,会组装好一个对象放入到全局的阻塞队列中,并且在这里我做了一个简单的限流,保证每放入8000个会休息一秒钟。

发送kafka消息这一块:

通过全局的公共原子变量来判断hive表数据是否导入完成,然后在循环里处理阻塞队列里的数据,并且每4000条数据才批量发送一次kafka消息。

 

优化之后的cpu以及gc情况得到了非常明显的改善,如下图所示:

gc次数没有当时截图,老年代增长的速度非常缓慢,full gc恢复正常,新生代gc4秒一次,也很正常。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值