进程CPU使用率报警

JavaProcessCpuUsageAlertV2

背景知识 (background)

无论是物理机或容器化部署的应用,最终都是混合部署的,共享CPU资源,部分应用CPU使用过多,会拖累其他应用的稳定性。

CPU使用率的取值范围是 [0 , 1],最大100%,衡量的是单个进程对整个物理机CPU资源的占用情况 ( 进程占用的CPU/所有CPU可用时间 )。

目前单进程超过20%且持续2分钟,则会触发报警。

大部分JVM应用是多线程、IO密集型,建议进程CPU控制在10%以内(排除定时任务),超过则考虑优化代码或扩容节点。

进程CPU使用率过高除了影响其他混部应用外,也可能被CGroup限制CPU,慢请求增多。

查看指标 (dashboard)

访问 kemonitor -> 应用 -> 监控狗 -> 节点监控,点开后,右上角可调整时间段。

展开: 进程 / 线程 / CPU / 磁盘 / IO这一行,找到监控面板:CPU Usage

其中,CPU Quota Usage - 进程即已分配给进程的CPU额度现在用了多少,CPU Usage - 进程为进程对整个物理机CPU的占用情况。

另外,开发也应关注监控面板: CGroup 统计[1m]里的近一分钟CPU Throttled占比,这个指标可侧面印证进程是否因CPU额度提前用尽而被限制了。

止损措施 (action)

关注 FAST / 7层的499、监控狗的慢请求报警,若受到影响,可临时摘流节点

事后改进(postmortem)

大部分JVM应用的CPU使用率在5%以内,不少应用甚至在1%以内。除了凌晨的定时任务,一般进程的CPU使用率波动不大。

CPU问题可借助 VisualVM 的抽样器或 arthas的火焰图,采样CPU,方便快速定位。

大部分情况下,可归结为不合理的代码设计,参考原因 (cause) 部分针对性优化。

可能的原因 (cause)

进程CPU使用率的报警,关注的是单个应用对其他混部应用的潜在影响以及不合理的设计对资源的浪费。

一、定时任务、计算逻辑过多

我们建议定时任务尽可能在凌晨的流量低峰期执行,避免白天的流量高峰期,减少对其他应用的影响。

若无法避免高峰时段执行,可尝试任务分片、隔离部署,比如,薪酬月初算薪,进程CPU使用率达到40%,k8s容器部署的话,月初可临时调度薪酬进程到独享的物理机,和其他应用隔离,算薪完成了再调度回去,释放资源。

另一种情况是,业务加载大批量数据、在内存里聚合计算的场景,建议分批加载、预计算,避免内存和CPU占用过多。

若涉及到批量加解密,建议限制单节点流量,尽可能多部署节点。

二、GC频率过高

通常是内存分配压力大, YoungGC频率过高,比如:ParNew / G1。

过于频繁的垃圾回收,则会占用更多CPU。最严重的是垃圾回收时,CPU时间片耗尽,暂停GC活动,GC日志里却停顿正常,可参考 CGroup 指标里近一分钟进程CPU Throttled 触发的次数来交叉验证。 see application-pauses-when-running-jvm-inside-linux-control-groups

对 ParNew / CMS组合来说,其设计目标就是低停顿,相应的Young区内存占用小,Young GC频繁是合理的,建议一分钟Young GC次数控制在20次以内,单次停顿在30ms以内。

内存分配压力大,可能是每次查询返回的数据过多、批量导入导出时内存数据过多。

导入导出时,我们建议确保内存里的数据在任意数据量下都是稳定、固定的开销,尽可能Stream、逐批写入;分页或List接口,确保在任意查询条件下返回合适的数据量,杜绝返回全量数据。

作为临时方案,可申请套餐扩容,增加CPU额度或内存。

若不是代码设计问题,可尝试调整GC线程数(ParallelGCThreads),以应用的CPU额度为基准,建议灰度一个节点观察。

三、线程数上升

线程数不是多多益善,Runable状态的线程越多,操作系统需同时调度的任务就越多,线程上下文切换的成本虽然比进程小,但依旧会消耗不少CPU。

无论是节点流量突增,还是慢请求过多,对稳定性有要求的项目,需确保在任何情况下,线程数量都是稳定的,尽可能Fail Fast。

GC线程、Netty Eventloop线程数,建议以CPU额度为基准,默认以物理机CPU个数为基准可能导致线程过多。

jdk8u121以下版本不支持container-aware,部分组件,比如Netty、GC算法以Runtime.getRuntime().availableProcessors()来设置并行度。

+XX:ParallelGCThreads用于STW时GC并行处理的线程数,默认算法是max(8, 8+ (5/8) * NCPU),我们建议NCPU(CPU数量)以进程的CPU额度为准。

+XX:ConcGCThreads是Concurrent Phase阶段的GC并行处理的线程数,默认为ParallelGCThreads的四分之一,如果并发标记耗时长,可适当调大观察GC耗时。

对Web容器来说,比如 Tomcat/Undertow等线程池 ,最大线程数控制在800个以内。业务自定义线程池的,建议设置合理的队列大小和最大线程数,并严格控制网络IO超时。

WEB应用通常对延迟敏感,若用户或调用方已取消请求,那么线程池/队列里等待的越久越浪费资源,反而会挤占后续到来的新请求,甚至会妨碍服务恢复正常。

总之,我们建议横向扩展,多部署一些节点,尽可能避免单个节点占用过多资源(CPU/内存)。

参考资料

results matching ""

    No results matching ""