ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RocketMQ消息堆积排查与治理:从定位到扩容再到治本

RocketMQ消息堆积排查与治理:从定位到扩容再到治本 1. 先别急着加机器把“堆积”这件事看透RocketMQ 消息堆积几乎是每个做电商、做交易、做日志收集的团队都绕不开的坎。面试官问这个问题表面上考的是“你会不会扩容 Consumer”实际上想听的是一整套排查链路堆积到底卡在哪个环节是 Broker 写不进去还是 Consumer 拉不动是消费逻辑本身太慢还是下游依赖把线程池拖死了答不出这层背再多“增加 Consumer 数量”的八股都没用。先明确一个概念RocketMQ 的堆积本质上就是Consumer 的消费速率长期低于 Producer 的生产速率导致消息在 Broker 的 CommitLog 里越攒越多消费位点ConsumerOffset和最大位点MaxOffset之间的差值不断变大。这个差值就是我们常说的“堆积数量”在控制台里能看到曲线一路飙升。但这只是表象。真正要命的是堆积往往不是单一原因而是多因素叠加。我遇到过最典型的一个案例业务方说“订单消息堆积了 10 万条”我上去一看Consumer 的线程池配的是 20消费单条消息要调远程接口接口 P99 是 300ms单线程每秒只能吃 3 条20 个线程满打满算也就 60 TPS。而 Producer 那边峰值每秒写入 500 条堆积自然只会越来越多。这种情况你哪怕把 Consumer 扩到 100 个实例如果线程数不变、下游耗时不变照样白搭。所以处理堆积的第一步永远是先定位而不是先动手。这里我建议按三个层面去排查Broker 层看 Broker 的写入性能是否正常有没有频繁 GC、磁盘 IO 打满、PageCache 压力过大。Consumer 层看消费线程数、消费逻辑耗时、拉取消息的阻塞情况、线程池活跃度。下游依赖层看消费时调用的数据库、Redis、远程 RPC 接口是否存在慢查询、连接池耗尽、超时重试等问题。这三层里Consumer 层是 80% 堆积问题的根源。为什么因为 Broker 的写入路径是顺序写盘 PageCache 加速只要磁盘没坏写入吞吐基本不是瓶颈真正拉垮的往往是消费端那种“单条消息处理太重”的设计。比如你在消费回调里做了同步的 HTTP 调用每次等 2 秒超时那线程池瞬间就被占满堆积就是必然的。还有一个容易被忽略的点RocketMQ 的消费并发模型。如果你用的是 DefaultMQPushConsumer它内部是一个拉取线程不断向 Broker 拉消息然后丢进一个线程池去执行消费回调。线程池默认的核心线程数、队列大小你都可能没调过结果就是拉取线程拉得飞快线程池里的任务却处理不过来消息全堆在本地队列里Consumer 进程本身就成了堆积点。这种情况连堆积曲线都看不出异常因为 Broker 端没积压但消费延迟照样高。我自己处理这类问题有个习惯先看 Consumer 的线程池活跃度再看消费单条消息的平均耗时最后才看堆积总量。把这三个数字列出来问题基本就水落石出了。下面把每个环节的具体套路拆开讲。2. 定位堆积根源三步排查法把瓶颈钉死2.1 第一步区分“生产堆积”还是“消费堆积”很多人一看到堆积就急着去扩容 Consumer这是典型的“头痛医头”。实际上RocketMQ 的堆积可能发生在生产端也可能发生在消费端两者的处理方式完全不同。生产堆积的表现是Producer 发送消息的耗时持续走高重试频繁Broker 的写入 TPS 明显下降。这种情况通常是 Broker 磁盘性能不行或者 PageCache 被写脏数据挤占得太厉害。你可以用mqadmin topicStatus命令查看 Topic 的写入情况或者直接监控 Broker 的写入延迟指标。消费堆积的表现则恰恰相反Broker 写入 TPS 正常但 ConsumeQueue 的消费进度落后越来越多Consumer 实例的消费 TPS 远低于生产 TPS。这是最经典的堆积场景后面所有步骤都围绕它展开。怎么快速区分很简单看两个指标Broker 端的消息生产速率和Consumer 端的消息消费速率。前者大于后者堆积必然发生两者持平堆积数量会维持不变前者小于后者堆积会被慢慢消化。把这组数据拉出来一对比方向就有了。2.2 第二步Consumer 侧细看线程数、耗时、阻塞三件套确定了是消费堆积接下来就要把 Consumer 解剖开。第一看线程数。如果你用的是DefaultMQPushConsumersetConsumeThreadMin和setConsumeThreadMax这两个参数决定了消费线程的上下限。很多时候大家都只设置了 Consumer 的实例个数忽略了单实例内部的线程数。假设你有 10 个消费实例每个实例 20 个线程那就是 200 个并发如果每个实例只默认了 5 个线程那就是 50 个并发差距四倍。所以扩容 Consumer 之前先确认单实例的线程池是否拉满。第二看消费耗时。给消费回调里的每个方法调用打上耗时日志或者用 Arthas 的trace命令看消费链路里哪一步最慢。我见过最夸张的案例消费逻辑里同步调了三个下游服务每个都设置了 5 秒超时三个串行下来最坏要 15 秒才处理完一条消息。你想想就算给你 100 个线程100 条消息同时卡在远程调用上整个消费集群基本等于瘫痪。这种问题靠加机器是治标不治本必须改异步化、批量处理或者走降级方案。第三看阻塞。RocketMQ 消费线程池默认用的是LinkedBlockingQueue队列长度默认是 1000。如果线程数不够任务就会在本地队列里排队排队越多消费延迟越高。这个本地队列的积压Broker 端是看不到的只有通过 JMX 查看线程池的队列大小才能发现。我建议把消费线程池的监控指标挂到监控平台重点盯queueSize、activeCount、completedTaskCount三个值。2.3 第三步下游依赖的体检数据库和远程调用不能漏消费逻辑一般都要读写数据库、调 RPC、操作 Redis这些下游服务的健康状况直接决定消费速率。数据库方面最常见的坑是慢 SQL和连接池打满。每条消息都要执行一次 INSERT 或 UPDATE本来应该走索引秒回结果因为没加索引、或者查询条件用了函数导致全表扫描一次就要 500ms。200ms 的延迟可以忍500ms 以上就会肉眼可见地拉低消费速率。用慢查询日志把这类 SQL 揪出来加上索引、优化 SQL通常能立竿见影。Redis 方面坑在大 Value和热点 Key。如果你在消费逻辑里对同一个热点 Key 做频繁读写Redis 单线程模型下所有请求都会被串行化排队延迟从 1ms 飙到 100ms 都是常见的。这种情况要么把热点 Key 拆散要么做本地缓存要么把聚合操作放到消费完成后异步处理。远程 RPC 方面最怕的是超时重试风暴。消费线程调 RPC 接口超时设为 3 秒失败后框架自动重试 3 次那一条消息在最坏情况下要 12 秒才能确定失败。更重要的是RPC 超时后线程不会立即释放它要等够超时时间才走重试逻辑这段时间里线程池资源被死占。我见过一个团队消费逻辑里调了个成功率只有 60% 的接口堆积曲线直接飙升到 200 万条最后把接口修好堆积才慢慢消化掉。所以消费逻辑里对远程调用的超时时间、重试次数一定要做严格限制该降级就降级。3. 应对堆积的核心手段扩容、重平衡、重置位点三把刀定位完原因接下来就是动手处理。根据堆积的程度和业务容忍度我一般把手段分成三级轻量级扩容、主动重平衡、重置消费位点。逐个说。3.1 轻量级扩容先调线程池再考虑加实例如果堆积数量不大比如几万条而且下游依赖一切正常这时候最经济的手段是先调 Consumer 的消费线程数。假设现在是 10 个消费实例每个实例 10 个线程一共 100 并发。你把每个实例的consumeThreadMax从 10 调到 40并发数就变成了 400消费速率理论上可以翻四倍。但注意一个前提下游凭依要能扛住这四倍的压力。数据库连接池够不够远程接口的 TPS 上限是多少如果下游扛不住调线程数只会让消费从“堆积”变成“大量超时失败”情况反而更糟。如果线程数已经是合理的比如每个实例跑 20-30 个线程堆积量仍然在涨那就需要加消费实例了。加实例要注意 RocketMQ 的负载均衡机制同一个 ConsumerGroup 下的实例会均摊 Topic 下的队列MessageQueue默认的AllocateMessageQueueAveragely策略会把队列平均分配给每个实例。假设 Topic 有 16 个队列当前有 4 个消费实例那每个实例分到 4 个队列。你加到 8 个实例每个实例分到 2 个队列单实例负载减半消费速率自然提升。但这里有个脑袋坑如果队列数本身少于实例数加实例就没用了。16 个队列最多只能被 16 个实例平分加到 32 个实例也只会出现一部分实例没有队列可分白白浪费资源。所以扩容之前先确认 Topic 的队列数是否 ≥ 实例数。如果队列数不够还得先加队列但队列数只支持在创建 Topic 时指定最大数量运行期可以改但影响面较大建议提前规划好队列数。3.2 主动重平衡让新实例尽快接活儿的技巧默认情况下新实例启动后要等 RocketMQ 的 Rebalance 机制自动触发这个时间默认为 20 秒RebalanceInterval。如果你在业务高峰期扩容这几秒甚至几十秒的延迟都会让堆积进一步加剧。想要尽快让新实例参与消费可以调整两个参数pollNameServerInterval和heartbeatBrokerInterval。前者控制客户端轮询 NameServer 获取路由信息的时间间隔默认 30 秒后者控制向 Broker 发送心跳的频率默认 30 秒。在集群规模不大、不会给 NameServer 造成压力的情况下可以把这两个值调小到 10 秒甚至 5 秒新实例能在更短的时间内获取到最新路由并触发重平衡。另外还有一个容易忽略的骚操作直接调用consumer.rebalance()方法。RocketMQ 客户端 API 里并没有一个公开的rebalance()接口让你手动触发实际的 Rebalance 逻辑在RebalanceImpl里普通 API 拿不到。所以实操上更靠谱的做法是把新实例的启动时间错开先启动一个观察它的消费情况确认正常后再启动下一个避免多个实例同时触发 Rebalance 造成队列分配的抖动。这里面还有个真实遇到过的情况新实例启动后明明同一个 ConsumerGroup 下已经有多个实例存在但新实例的消费 TPS 很低甚至一直没有消息进来。原因通常是重平衡还没触发或者是订阅关系不一致。比如新实例的subExpression写的是TagA而老实例写的是TagB两者订阅的 Tag 不同重平衡时就会把队列分给不同 Tag 的实例导致看起来“新实例没消息”。这种问题排查起来很反直觉后来我们养成了习惯新实例的订阅配置一律从配置中心复制绝不手填。3.3 重置消费位点堆积太多时的兜底大杀器当堆积量已经达到几百万甚至上千万条靠调线程、加实例慢慢消费可能需要好几个小时业务等不起。这时候有两个选择跳过堆积消息或者从最新位点开始消费。跳过的做法是重置消费位点。在 RocketMQ 控制台的 Consumer 管理页面里可以对指定 ConsumerGroup 的消费位点进行重置。重置有两种模式从最新位点开始消费也就是跳过所有堆积消息只消费新消息。适合堆积消息已经失去业务价值、或者后续消息依赖后续状态才能处理的场景。从指定时间点开始消费比如你确定 13:00 之后的消息是完整的可以把消费位点重置到 13:00 左右丢弃之前的脏数据或无效数据。重置位点是高风险操作执行前必须确认三个问题被跳过的消息是否真的可以丢弃如果下游对数据完整性有强要求比如交易流水、订单状态变更跳过之后会产生数据不一致这种场景不能用。是否有多个消费实例在跑重置位点是针对 ConsumerGroup 级别的如果多个实例负载不均衡重置后可能有的实例消费进度被调整有的没有导致重复消费或者漏消费。业务方是否已经确认任何重置操作都应该走变更流程至少要有业务负责人的口头确认和邮件记录。我遇到过一次紧急情况线上堆积了 2000 万条某业务 Topic 的消息消息内容是一个小时内拍下的商品链接快照但实际上这些快照在数据库里都有实时状态堆积消息里的数据已经过期。当时经过业务确认后直接把消费位点重置到了最新堆积归零整个消费集群恢复了健康。这个例子很极端但足以说明重置位点是处理大规模堆积时最有效的兜底手段没有之一。4. 治本之道消费逻辑优化和链路治理的实操细节堆积极少是“运气差”造成的绝大多数是设计和代码的债。跟在后面一次次应急不如把消费逻辑本身变稳。分享几个我自己压箱底的做法。4.1 消费逻辑里少做重活儿能异步就异步RocketMQ 消费回调里你做的事越少消费速率越快这是铁律。常见的高危操作包括在回调里同步调用第三方 HTTP 接口在回调里做复杂计算、大文件读取在回调里批量执行数据库写操作但不分批在回调里依赖另一个 MQ 的消费结果这里最典型的反面案例就是“消费回调里发短信/发邮件”。很多人觉得发短信很快实际上短信渠道的 HTTP 接口延迟经常在 500ms 到 2 秒之间而且短信服务在高峰期还会限流。如果把发短信写在消费回调里消费速率瞬间被拉低。我的做法是消费回调只负责把消息内容持久化到本地表再发一个内部标记真正的短信发送由定时任务或单独的异步线程池去处理。这样消费回调全程耗时控制在几毫秒以内堆积永远不会发生。还有一类重活儿是数据库写入。如果消费逻辑要批量插入数据强烈建议用batch方式一次插入 100 条甚至 500 条而不是一条一条 insert。假设单条 insert 是 2ms100 条就是 200ms改成 batch 后100 条可能只需要 10ms性能提升 20 倍。如果你是用 MyBatis写一个foreach批量 insert 的 SQL 并不难收益却非常夸张。4.2 重试要有上限死信队列是最后的收尸人消费失败导致的重试如果不加控制会无限占用消费线程把堆积越拖越重。RocketMQ 默认的重试机制是消费失败后会按照RETRY_TIMES重新投递默认最多重试 16 次每次间隔时间逐步拉长。这本来是保护机制但如果你在消费回调里一直抛异常又不理阴界限16 次重试会消耗大量消费线程和网络资源。我的建议是业务消费失败要区分“可重试”和“不可重试”。对于订单状态没同步之类的不一致数据可以重试对于消息本身格式错误、参数非法之类的数据重试 100 次都是白搭。这类消息应该直接捕获异常记录日志然后走 RocketMQ 的死信队列DLQ让专门的运维人员定期处理。死信队列的名字一般是%DLQ%ConsumerGroupName里面的消息有完整的原始内容和消费失败原因。我通常会写一个死信队列的消费程序把这些消息按照业务类型自动分类自动恢复的就重新投递到原 Topic无法自动恢复的钉钉告警让研发手工介入。这套机制跑通之后堆积问题至少减少一半。4.3 消费链路里做“水位预警”别等堆起来才动手我在每个消费集群上都设了四道预警线四道线的经验值是预警级别触发条件响应动作提示堆积数量 5000检查消费速率是否正常警告堆积数量 50000评估是否需要扩容 Consumer严重堆积数量 100000拉业务方和运维一起排查链路紧急堆积数量 上百万且持续增长执行重置位点或临时停 Producer 限流监控体系是处理堆积的第一道防线等招聘的面试官都开始问“堆了怎么办”的时候你肯定不希望是自己在生产环境里慌忙救火。水位预警怎么做最简单的方式是创建消费者来获取消费位点再通过 RocketMQ 的mqadmin consumerProgress命令定期采集计算每个 ConsumerGroup 的堆积差值写入监控系统。更现代的做法是接入 RocketMQ Dashboard它自带消费进度监控和告警功能界面直接能看到某个 ConsumerGroup 的消费延迟。如果你是自研的监控系统那核心逻辑就是埋点采集ConsumerOffset和MaxOffset两者之差超过阈值就触发告警。四道水位的预警级别里第三级开始就必须有明确的处理 owner第4级需要触发紧急预案流程。这个过程一定要固化到文档里不然每次都临时拉人开会效率极低。5. 从一次真实的线上事故看完整处理流程理论讲了这么多还是用一个完整案例把整个过程串起来读起来更直观。某天下午 3 点左右监控告警弹出交易中心某个 Topic 的 ConsumerGroup 堆积量在 20 分钟内从 2 万涨到了 33 万。这个 Topic 承载的是订单状态变更消息消费逻辑里会同步调用户中心的 RPC 接口获取用户信息然后写库。我当时的排查流程是这样的第一步打开监控看生产速率和消费速率。生产速率大约每秒 800 条消费速率只有每秒 120 条生产远大于消费堆积在预期内问题出在消费侧。第二步看消费线程池指标。发现 30 个消费线程里面有 28 个处于RUNNABLE状态但是从线程栈看几乎全部卡在HttpURLConnection.getInputStream上也就是远程 RPC 调用处。这不难判断用户中心接口的耗时在飙升。第三步查看用户中心接口的监控发现 P99 从平时的 80ms 涨到了 3 秒明显是下游出问题了。联系用户中心负责人后发现该接口所在的集群下午发布了一个版本带了一个性能回退的 bug导致接口处理变慢。我当时没有直接重启消费进程而是先做了两件事第一把消费逻辑里的远程调用超时时间从 3 秒改到 1 秒超时直接走降级方案本地缓存用户信息第二临时把消费线程数从 30 调到 60尽量提高短时消费速率。这样改完之后消费速率从每秒 120 条慢慢回升到了 400 条左右堆积曲线开始掉头向下。用户中心那边修复后又过了一个小时消费速率恢复到每秒 750 条左右堆积彻底清完。这中间有一个细节改消费线程数可以在运行期内通过updateConsumerThread向 Broker 提交新的线程数配置吗不能这个参数是 Consumer 启动时就固定的你要改它必须重启消费实例。所以我当时是起了一个新的 ConsumerGroup 临时实例或者直接改了配置重启。不管哪条路都要接受短时间内可能重复消费几万到几十万条消息的风险。针对这种运行期调整需求我们后来做了一套通用方案消费线程数、消费超时时间、远程调用超时时间全部做成配置中心动态配置调整时不用重启消费进程直接推送新配置线程池和客户端参数实时变更。这个方案上线后处理堆积的响应时间从半小时缩到了一分钟。这个案例给我们的启发堆积问题的根子往往不在 RocketMQ 本身而在离它最近的消费代码和下游依赖上。你能控制的是消费线程数、超时时间、批量大小、异步化程度但下游能不能扛住压力取决于你们团队的链路治理水平。这也是为什么很多团队用上了 K8s 自动扩容堆积照样还是会发生。6. 面试官想听什么从应答框架到避坑细节既然题目是“面试官问消息堆积怎么处理”我猜读者里至少有半数是想把这个问题答到面试加分水平的。那就按面试场景拆一下哪些话该说、哪些坑别踩。6.1 回答框架先定位再扩容再治本面试时听到这个问题千万别一开口就“加机器”。我建议按下面这套逻辑递进回答既有思路、又有细节、还能体现经验先描述堆积的本质生产速率 消费速率导致未消费消息在 Broker 上积压。再给出定位链路先看生产速率和消费速率的差值再查消费线程池活跃度、消费单条耗时、下游依赖耗时最后判断是哪一个环节成了瓶颈。然后说应急处理如果是临时问题比如下游抖动可以调消费线程数、加消费实例、用死信队列接收失败消息甚至重置消费位点跳过无用消息。最后说长效治理代码层面做异步化、批量写、失败快速降级监控层面部署消费水位告警压测层面定期模拟全链路压力测试。这套框架能充分展示你不光会背 API而是真的能对整个消息链路做诊断。6.2 加分项主动聊“顺序消费”和“幂等”如果你能在答案里自然地带出 RocketMQ 的两个进阶主题面试官会有明显的好感。一个是顺序消费。顺序消费和堆积是天然矛盾的它要求同一个 Queue 里的消息只能被同一个消费线程串行消费所以哪怕你有 100 个消费线程能用的也只是一个。面试时你可以说“如果业务要求全局顺序消费那堆积的应对手段会受限这时候更多要考虑把 Topic 分区设计得合理把不需要全局顺序的业务拆出来。” 这段能体现你想得比“贪多求快”更深一层。另一个是幂等消费。堆积处理过程中重置位点、重复投递、失败重试都会导致消息被消费多次如果消费逻辑不是幂等的就会产生重复数据。我建议你在消费回调里顺手实现一组幂等校验比如用唯一业务 ID 查 Redis 是否存在存在就直接返回消费成功不在才执行真正的逻辑。这样无论消息被重复投递多少回都不会产生脏数据。面试时提到这一点基本就是“有真实生产经验”的明证。6.3 避坑点千万别踩的三个雷面试中常有候选人聊得兴起最后死在两个细节上。我也列出来提醒自己团队的人免得赔了技术又折了 Offer。雷区一张口就说“日志消费端堆积加 Kafka partitions”。这是概念混淆RocketMQ 里对应的是 Queue不是 Partition。虽然两者都是分区概念但说错会显得你底子不牢。雷区二把“消息堆积”说成“消息丢失”。堆积的根本特征是消息还在 Broker 上只是来不及消费数据没有丢。如果回答里把这两者混作一团说明你压根没理解 RocketMQ 的存储模型。雷区三重提“延迟消息”时无脑消耗。如果业务用到了延迟消息堆积排查时要把延迟队列的调度情况单独拉出来看不能一体化运维。RocketMQ 的延迟消息是存在单独的 Schedule 队列里的它在到达执行时间之前不会进入真正的消费队列所以它产生的“堆积”是预期行为不需要处理。懂得区分“预期堆积”和“异常堆积”也是区分菜鸟和老鸟的信号之一。7. 实操中积累的监控脚本与习惯最后分享几段我日常排查堆积时必用的命令和脚本都是可以直接抄作业的东西。7.1 用 mqadmin 快速看堆积RocketMQ 安装包自带的mqadmin命令是排查堆积的第一个工具。按下面的流程快速获取关键信息# 列出所有消费者组的消费进度 mqadmin consumerProgress -n {namesrvAddr} # 查看指定消费者组每个队列的消费积压情况 mqadmin consumerStatus -g {consumerGroup} -n {namesrvAddr} # 查看某个Topic的队列分布和写队列数量 mqadmin topicStatus -t {topic} -n {namesrvAddr}consumerStatus会显示每个队列的当前 Offset 和最大 Offset两者差值就是队列维度上的堆积量。如果你发现某个队列的堆积量明显大于其他队列那就是队列负载不均原因可能是生产端的消息 key 全都路由到了同一队列或者消费端的重平衡把某个队列分给了性能差的实例。7.2 用 JMX 看消费线程池内部状态如果想让问题定位更快给 RocketMQ Client 进程开启 JMX 端口启动参数加-Dcom.sun.management.jmxremote.port9999然后用 jconsole 或者脚本读取以下 MBeanorg.apache.rocketmq.client:typeConsumergroup{group}里的QueueSize、ActiveCount、PoolSizeInvokeGetConsumerStatus可以获取所有队列的消费进度信息通过这些指标能直接看出线程池有没有被打满、任务在本地队列里积压了多少。我见过的情况是ActiveCount一直等于PoolSize说明线程全在干活基本可以判断是消费逻辑耗时造成的瓶颈。7.3 写消费告警脚本的通用模板如果你用的监控平台只能配简单规则没法直接读 RocketMQ 指标可以写一个定时执行的脚本去采集堆积量再对接企业微信/钉钉告警。下面是一个简化的 Python 模板换掉连接信息就能跑import subprocess import json import requests NAMESRV 127.0.0.1:9876 GROUP consumer_group_a THRESHOLD 50000 def get_accumulation(): cmd fmqadmin consumerStatus -g {GROUP} -n {NAMESRV} result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue) print(result.stdout) if __name__ __main__: get_accumulation()这个脚本只是骨架生产环境建议直接走 RocketMQ Dashboard 自带的监控告警或者用 Prometheus 抓取相关指标。但如果你手头只有裸集群临时用脚本顶上是完全可行的。7.4 一个容易被忽略的习惯关闭消费线程池自动扩展很多人在DefaultMQPushConsumer里把consumeThreadMin和consumeThreadMax设置成相同值比如都设成 30。为什么呢因为 RocketMQ 的线程池在consumeThreadMin和consumeThreadMax之间会按照负载自动调整线程数但调整频率和策略比较保守往往跟不上堆积的突增。把两个值设为相同等于告诉线程池不要自动伸缩直接固定线程数。这样线程池的行为可预期排查问题时少一个变量。实际生产经验也证明固定线程数比自动伸缩更稳定。最后分享一个我自己的处理习惯单独强调一点经验。遇到堆积我很少先发群消息“消费堆积啦大家谁的锅”。我的顺序永远是先把生产速率、消费速率、线程池活跃度、单条消费耗时、下游依赖耗时这五个数凑齐再判断问题属于“代码慢”、“下游慢”还是“容量不够”然后决定是调线程数、加实例、走降级还是直接重置位点。这套流程看起来简单但真正执行过的人会明白难的不是操作是冷静地凑齐数据再下判断。很多人一看到堆积就慌又是重启、又是清消息最后把问题越搞越大。只要你能按这个节奏来消息堆积不是大事它更像是一个信号你的消费链路哪里设计得不够稳该优化了。
返回列表