线上 GC 告警,我菊花一紧,裤子都没提!!

作者入职拼多多后首次遇到线上GC告警,告警显示新生代垃圾回收次数过多。经排查,定位到同步广告成交额信息的定时任务有问题,因重新封装数据会创建大量对象。作者先尝试拆分任务,后采用任务分片的方法解决问题,最后总结了此次处理告警的收获。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

640?wx_fmt=gif

            阅读本文约需要5分钟

 

就在上周,晚上下班后在家上厕所,突然微信企业邮箱收到线上GC告警了:G1 Young Generation Count 超过阈值。我菊花一紧,裤子还没来得及提。这是我入职拼多多后第一次遇到的线上告警。

 

从告警提示来看,是新生代垃圾回收次数过多,换种角度想想,应该是代码中某个地方创建了太多的对象,而且很快就被回收。由于电脑没装 VPN,只能第二天去公司看看了。

 

1. 调度出问题了?

 

第二天到公司,吃过饭大概1点左右又告警了一次。从CAT监控平台看,确实有两次高峰,而且超过阈值。初步判断,可能是调度不均匀导致的,因为这不是那种必现的频繁告警。只要调度均匀,线上几台机器各自分担点,其实也还好。因为确实有这种可能,某个时刻都调度到某一台机器了,导致负载太大,大量的创建和回收对象。

 

我正准备去找调度那边的同事咨询。老大跟我说,有没有可能是任务本身的问题?也就是说不管调度到哪台机器执行,它都会告警,任务本身就有问题。我觉得也有道理。

 

2. 问题的定位

 

因为告警的服务是我的定时任务,这个服务里有三十几个定时任务在被调度。所以首先我得找到是哪个定时任务出的问题,于是我根据告警时间,去线上的可视化日志平台调取两次告警前后的日志。

 

结合代码 Command 接口的日志提示,根据关键词快速搜索出打印的 Command 日志(如果不懂 Command,可以看下这篇文章:程序员除了会CRUD之外,还应该知道什么叫CQRS!),因为我只要定位到了 Command,就好定位是哪个任务了。因为不同类的任务会调用不同的 Command 去执行。

 

通过两次告警日志的定位,最后分析出是同步广告成交额信息的定时任务出的问题。这个定时任务是干啥的呢?首先去今日头条拉取今天、昨天、前天和大前天的广告交易额数据,然后重新封装,上报到其他数据平台。问题就在于这中间的重新封装,会创建大量的对象。

 

要知道拼多多的交易额数据是非常多的。虽然已经在代码里限定了同步粒度是1000条一次,但是还是非常频繁的创建和销毁对象。而且数据必须重新封装,这是无法避免的,那怎么解决呢?

 

3. 将任务拆分

 

第一反应就是将原来的任务拆分,粒度拆分更细一点。什么意思呢?比如原来同步昨天、前天和大前天的数据是放在一个任务里执行的,那我拆成三个任务去执行。如下图:

 

640?wx_fmt=png

 

因为拆分任务后,三个任务都会被调度的,不同的任务就有可能被调度到不同的机器去执行,这总比在一台机器上执行要好很多。这是从任务粒度的角度去解决,把任务分的更细,这个方案是可行的,也是有效果的。

 

但是就在昨天,又一次告警了……说明把任务拆分后,虽然可以降低负载,但是仍然没法满足预设的阈值。

 

4. 将任务分片

 

上面说的把任务拆分,指的是拆分后,每个任务会被调度到某台机器去执行。那么将任务分片是什么意思呢?任务分片指的是,一个任务我让线上所有机器去执行,你执行一点,我执行一点,最后保证一个任务被完整的执行掉即可。如下图:

 

640?wx_fmt=png

 

这种思路是非常棒的,公司的Gavin调度平台做的非常好,我学习了一下公司相关的文档,再加上和同事的讨论,搞清楚了这个分片的原理。我简单抽象一下如何将任务分片去让所有机器调度,而且保证任务的完整性。

 

假如线上有两台机器A和B,我将一个任务分成10片,那么每个机器分到的片数集合可以表示成 [0,1,2,3,4] 和 [5,6,7,8,9]。那么在我的任务里,假设要处理14267条数据(我随便敲的一个数字),每条数据应该都有一个标识,假如就是我们常用的id,那我用id去模10,得到的结果落在哪个集合,就让该集合对应的机器去执行。

 

这相当于我把这14267条数据给打散了,放到所有机器上去各自执行一点,就像上面图中展示的那样,而且是没有规律的打散。所以我们只要知道分了多少片以及每台机器被分到的片数集合,即可完成这个分片功能。

 

但是得确保该任务可以被打散执行才可以,比如某个任务需要计算好多个账号在一起的相关费用,那么就不适合这种分片的方式了。所以方式和理念固然重要,但是也要结合实际场景来用。实在不行,那就再加个机器呗!

 

5. 总结一下

 

这次告警的处理对我来说,收获蛮大的,主要有几点感触蛮深的。

 

1)公司有非常强大的监控平台,任何一个微服务的运作情况,都清晰的展现在平台上,而且每人轮流巡视平台,遇到告警,会第一时间告知对应负责人,平台也会自动给负责人发通知。保证第一时间暴露问题。

 

2)调度平台非常重要,我之前参与的一个项目也有很多定时器,但是缺少一个统一的调度平台,代码里定时器乱飞,这里有个@Scheduled,那里也有个,有时候你根本不知道哪个定时器在执行。

 

3)文档非常重要,公司规不规范很大一部分取决于自己文档平台的建设,而不是写个word我传给你,你传给我。公司需要有自己的wiki,每个平台有对应的文档,这样沟通和学习,效率更高。

 

4)在咨询调度相关的问题时,加了公司的Gavin调度技术群,跟管理员学习了很多,公司基础架构都有对应的技术群,遇到问题都会有技术支持,很方便。

640?

 

程序员除了会CRUD之外,还应该知道什么叫CQRS!

阿里面试,我挂在了第四轮……

同样是程序员,为什么别人比你更优秀?

最常用的经典数据结构和算法汇总

2018年所有精华文章汇总,错过了血亏!

 

 

                                                                                             关注我

                                                                                      每天进步一点点

 

640?wx_fmt=png

 

点赞是最大的支持 640?wx_fmt=gif

<think>首先,用户的问题是:如何优化Kafka配置以避免频繁的垃圾回收(GC)告警。用户到了Kafka中的监控和警报,特别是GC告警问题。 从系统指令中,我需要: - 使用中文回答。 - 结构清晰,逐步解决问题。 - 保证回答真实可靠。 - 在行内数学表达式使用$...$格式,但这里可能不涉及数学表达式。 - 独立公式使用$$...$$格式,但同样,可能不适用。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,如[^1]。 参考的引用内容: - 引用[1]:关于Kafka监控和警报的重要性,包括主动问题检测、快速响应、维护SLA等。 - 引用[2]:关于观察GC细节的命令:-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/kafka/gc.log 用户的需求是优化Kafka配置以减少GC告警GC告警通常是由于垃圾回收频繁发生,导致性能问题。我需要基于Kafka的优化建议来回答。 关键点: - Kafka是用Java写的,所以GC是JVM相关的。 - 优化包括调整JVM参数、Kafka配置等。 结构化回答: 1. **理解问题**:解释为什么GC告警发生,常见原因如内存不足、配置不当。 2. **诊断步骤**:如何监控和分析GC问题,使用引用[2]中的命令。 3. **优化建议**:供具体的配置调整。 4. **验证和监控**:如何验证优化效果。 5. **生成相关问题**:在最后添加相关问题。 优化建议包括: - **JVM堆大小设置**:调整-Xms和-Xmx参数。 - **GC算法选择**:如使用G1GC代替CMS或Parallel GC。 - **Kafka配置**:如调整batch.size、linger.ms等以减少对象创建。 - **其他建议**:如避免不必要的日志、优化生产者/消费者配置。 确保回答真实可靠:基于Kafka官方文档和最佳实践。 最后,添加引用标识:在引用参考内容时添加。 现在,构建回答: - 开头:简要重述问题。 - 主体:分步骤解释。 - 结尾:总结并生成相关问题。 生成的相关问题: - 基于主题,如如何监控GC、其他Kafka性能优化等。</think>### 优化Kafka配置以避免频繁垃圾回收(GC)告警 Kafka作为基于Java开发的分布式消息系统,其性能高度依赖于JVM的垃圾回收机制。频繁的GC告警通常是由于内存管理不当、对象创建过或JVM配置不合理导致的,这不仅影响吞吐量和延迟,还可能触发服务中断[^1]。以下是逐步优化Kafka配置以缓解GC问题的结构化方法。本建议基于Kafka官方文档和行业最佳实践,确保可靠性和可操作性。 #### 步骤1: 诊断GC问题根源 在优化前,必须先分析GC行为,以确定瓶颈。使用JVM参数监控GC日志是基础步骤(参考引用[2]): - 添加JVM启动参数:`-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/kafka/gc.log`。这将记录详细的GC事件到指定文件。 - 分析日志: - **Full GC频率**:如果Full GC发生频繁(如每分钟超过一次),表明堆内存不足或内存泄漏。 - **Minor GC耗时**:如果Minor GC耗时过长(如超过100ms),可能因年轻代大小不当。 - 使用工具如`gceasy.io`或`jstat`可视化日志,识别峰值和模式。 - 监控关键指标: - Kafka自带的JMX指标:`kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec`(消息率)和`java.lang:type=GarbageCollector,name=*`(GC计数和时间)。 - 结合Prometheus和Grafana设置告警阈值(如GC暂停时间 > 200ms时触发)[^1]。 诊断后,常见原因包括堆大小过小、GC算法不匹配或Kafka生产者/消费者配置导致对象膨胀。 #### 步骤2: 优化JVM配置 JVM参数是减少GC的核心。以下是推荐设置(以Kafka 3.x版本为例): - **调整堆大小**: - 初始堆(`-Xms`)和最大堆(`-Xmx`)应设置为相同值以避免动态调整开销,例如:`-Xms8g -Xmx8g`。大小取决于系统内存: - 对于生产环境,建议堆大小为物理内存的50%-70%,但不超过32GB(避免G1GC的region问题)。 - 示例:如果服务器有16GB内存,设置`-Xms8g -Xmx8g`。 - 年轻代大小:使用`-XX:NewRatio`控制(如`-XX:NewRatio=2`表示年轻代占堆的1/3),或直接指定`-Xmn`(如`-Xmn2g`)。 - **选择高效GC算法**: - 推荐G1GC(Garbage-First)作为默认,它适合大堆和低延迟场景: ```bash -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m ``` - `MaxGCPauseMillis=200` 设置目标暂停时间(单位毫秒),G1GC会尝试达成。 - `G1HeapRegionSize` 根据堆大小调整(堆 > 8GB时用16m)。 - 避免CMS(Concurrent Mark Sweep),因为它已弃用,且在高吞吐下易导致Full GC。 - **其他JVM优化**: - 减少对象分配:添加`-XX:+UseStringDeduplication`(字符串去重)和`-XX:+UseCompressedOops`(压缩指针,节省内存)。 - 监控参数:启用`-XX:+PrintGCApplicationStoppedTime`记录GC暂停时间。 - 完整示例JVM参数(添加到Kafka的`KAFKA_JVM_PERFORMANCE_OPTS`环境变量): ```bash export KAFKA_JVM_PERFORMANCE_OPTS="-server -Xms8g -Xmx8g -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/kafka/gc.log" ``` #### 步骤3: 优化Kafka Broker配置 Kafka的内部参数影响对象创建和内存使用。调整`server.properties`文件(路径通常为`/etc/kafka/server.properties`): - **减少内存压力**: - `batch.size`:生产者批处理大小(默认16KB)。增加此值(如`batch.size=32768`)可减少小批次数,从而降低GC频率。但需平衡延迟。 - `linger.ms`:生产者等待批处理的时长(默认0ms)。设置为`linger.ms=5`,允许更批处理聚合。 - `log.cleaner.dedupe.buffer.size`:如果启用日志压缩(log compaction),增加此值(如`1g`)减少清理时的GC压力。 - **控制网络和缓冲区**: - `replica.fetch.max.bytes` 和 `message.max.bytes`:增加(如`message.max.bytes=1000012`)以避免消息截断导致的重复对象。 - `num.replica.fetchers`:设置副本拉取线程数(如`num.replica.fetchers=4`),避免单线程瓶颈。 - **其他关键设置**: - `compression.type`:启用压缩(如`compression.type=lz4`),减少网络传输的数据量,间接降低内存使用。 - `num.io.threads` 和 `num.network.threads`:根据CPU核心数调整(如各为8),防止线程阻塞引发的对象堆积。 #### 步骤4: 优化生产者和消费者应用 GC问题常源于客户端。确保所有Kafka生产者/消费者使用最佳实践: - **生产者配置**: - 设置`buffer.memory`(如`33554432`)和`batch.size`,避免内存溢出。 - 使用异步发送(`send()`方法带回调),减少阻塞。 - **消费者配置**: - 调整`fetch.min.bytes`和`fetch.max.wait.ms`(如`fetch.min.bytes=65536`),减少拉取频率。 - 避免在消费者循环中创建临时对象(如重用反序列化器)。 - 通用建议:升级到最新Kafka客户端库(如3.5.0),修复已知内存问题。 #### 步骤5: 验证和持续监控 优化后,验证效果: - 重启Kafka Broker应用配置(`systemctl restart kafka`)。 - 重新分析GC日志:Full GC频率应降至每小时几次以下,Minor GC暂停 < 50ms。 - 监控工具:使用Kafka自带的JMX或第三方如Prometheus,设置GC告警阈值(如GC时间占比 > 10%时告警)[^1]。 - 压力测试:用`kafka-producer-perf-test.sh`模拟高负载,检查GC行为。 如果告警未消除,考虑更深层问题: - 内存泄漏:用`jmap`和`jhat`分析堆转储。 - 系统级优化:增加服务器内存或升级硬件。 - 专业工具:在复杂场景下,使用JProfiler或VisualVM进行实时分析。 通过以上步骤,大GC告警可显著减少,升Kafka集群的稳定性和性能。记住,优化需基于具体负载测试,避免一刀切设置[^1]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值