当前位置: 首页 > 科技观察

一次生产CPU100%排查优化实践

时间:2023-03-13 01:17:32 科技观察

前言年底真的很不顺利,最近接到运维告警:说明部分服务器负载很高,让我们定位一下问题。真是想来想去,前几天还特意增加了一些服务器的负载(没错,老大让我写个BUG!),还好不同环境互不影响。定位问题得到问题后,我先去服务器上查看,发现只有我们的Java应用在运行。所以先用ps命令获取应用程序的PID。然后用ps-Hppid显示这个进程的线程数。输入大写P将根据线程的CPU使用率对线程进行排序,从而产生以下结果。果然有些线程的CPU使用率非常高。为了方便定位问题,我马上使用jstackpid>pid.log将线程栈转储到日志文件中。我在上面100%的线程中随机选了一个pid=194283,转成16进制(2f6eb),然后在线程快照中查询:因为线程快照中的线程ID是以16进制存储的。我发现这是一堆Disruptor。前段时间刚刚解决了一个Disruptor队列导致的OOM:内存溢出是不是比Disruptor好?没想到又出来了。为了更直观的查看线程的状态信息,我把快照信息上传到一个专门分析过的平台。http://fastthread.io/有一个菜单,显示所有消耗CPU的线程。仔细一看,发现它们和上面的栈差不多。也就是说它们都是Disruptor队列的栈,都在执行java.lang.Thread.yield函数。众所周知,yield函数会让当前线程让出CPU资源,然后让其他线程去竞争。根据刚才的线程快照,发现大约有30个线程处于RUNNABLE状态,都在执行yield函数。因此初步判断是执行yield函数后大量线程相互竞争,导致CPU占用率升高,通过堆栈发现与Disruptor的使用有关。解决问题后查看代码,发现内部根据每个业务场景使用了2个Disruptor队列进行解耦。假设现在有7个业务类型,相当于创建了2*7=14个Disruptor队列,每个队列有一个消费者,即总共有14个消费者(生产环境更多)。同时发现配置的消费等待策略为YieldingWaitStrategy。这个等待策略确实会执行yield让出CPU。代码如下:初看好像和这个等待策略有很大关系。本地模拟为了验证,我在本地创建了15个Disruptor队列,结合监控观察CPU使用情况。创建了15个Disruptor队列,每个队列使用线程池向Disruptor队列收发100W条数据。消费者程序只是打印它。运行一段时间后,发现CPU占用率真的很高。同时dump线程发现和生产的现象也是一致的:consumer线程都处于RUNNABLE状态,同时在执行yield。通过查询Disruptor官方文档,发现YieldingWaitStrategy是一种充分压榨CPU的策略,采用自旋+yield来提升性能。当消费者线程(EventHandlerthreads)数量小于CPU核心数量时,推荐使用该策略。同时查看其他等待策略BlockingWaitStrategy(也是默认策略),采用锁机制,CPU占用率不高。所以在与之前相同的条件下,将等待策略更改为BlockingWaitStrategy。对比刚才的CPU,后面会发现使用率明显降低;同时dump线程后,会发现大部分线程都处于waiting状态。优化方案貌似把等待策略改成BlockingWaitStrategy可以减慢CPU的使用率,但是注意YieldingWaitStrategy官方的描述是这样写的:消费者线程数(EventHandler线程)小于CPU数时推荐使用该策略核心。在现有的使用场景下,很明显consumer线程的数量已经大大超过了核心cpu的数量,因为我的使用方式是一个Disruptor队列一个consumer,所以我把queue调整为只有一个再试(策略仍然是YieldingWaitStrategy)。运行一分钟后,发现CPU占用率一直比较稳定,不高。总结那么调查之后,我们就可以得出一个结论。要从根本上解决这个问题,我们需要拆分现有的业务;现在一个应用中同时处理N个业务,每个业务使用多个Disruptor队列。由于是在服务器上运行,CPU资源是共享的,会导致CPU占用率高。所以我们的调整方法是:为了快速缓解这个问题,先把等待策略改成BlockingWaitStrategy,这样可以有效降低CPU占用率(业务上也是可以接受的)。第二步,拆分应用(上面模拟的一个Disruptor队列),一个应用处理一类业务;然后分别部署,这样也可以相互隔离,互不影响。当然还有其他的优化,因为这也是老系统,这次dumpthread居然发现创建了800+线程。创建线程池的方式是核心线程数和最大线程数相同,导致部分空闲线程没有被回收;这会造成大量无意义的资源消耗。所以我们也会结合业务调整创建线程池的方式,减少线程数量,物尽其用。