生产 1 万 QPS、短信平台仅承受 100 QPS 时,如何结合 MQ 恒速消费并避免频繁回投?
未标记
考查重点:消费配额、背压与消息时效。按下游 100 QPS 预算安排拉取和发送,再计算积压增长与最老消息年龄,避免用失败重投实现限流
本题目录
参考答案
核心回答
MQ 用来可靠缓冲,短信发送端用全局配额控制出站速率。若平台要求平滑的 100 QPS,按约每 10 ms 放行一次或采用等效的平滑调度,再按可用配额拉取、分发消息;不要把没抢到令牌的消息立即消费失败、重新投递,形成忙重试。
还要先判断容量是否可持续:若生产长期为每秒一万条、出口只有每秒一百条,积压每秒增长九千九百条。队列只能延后处理,不能消除这个差额;必须结合业务时效做准入、合并、过期丢弃或增加下游额度。
消费端怎样配合
支持主动获取消息的客户端,可以先为下一批预留配额,再拉取不超过配额的消息;拉取量、在途消息数和本地缓冲都有上限。多个实例共享或分配同一份 100 QPS 额度,不能每个实例各放行一百条。
已经收到但暂时没有额度的消息,可以在有界调度队列中等到下一个发送时刻,并保证消息不可见时间或消费超时覆盖等待。更长的等待使用明确的延迟调度。PushConsumer 若由回调确认结果,应在真实处理完以后确认,不能先返回成功再把工作随意丢进内存线程。
并发数与发送速率分别控制。短信请求耗时不固定时,单纯把线程数设成一百,不能保证每秒只请求一百次;令牌桶若允许很大的桶容量,也可能产生短时突发,不等于恒速。
失败、确认与时效
平台接受发送后再按客户端契约确认消费;瞬态失败采用有上限的退避,参数错误直接分类处理。短信已发送但确认丢失时可能再次投递,因此需要稳定的短信业务标识,并尽可能使用下游的幂等能力。
若高峰持续一分钟,新增积压为 594000 条;之后停止生产,仅清理这些积压仍需约 5940 秒,也就是九十九分钟。验证码等短时消息显然不能按这个等待时间无限排队,应在发送前检查截止时间,并在入口处理超额需求。监控最老消息年龄、过期率、实际下游限流和成功率,比只看队列条数更有业务意义。
参考资料
- 国内:JavaGuide:线程池:采用工作队列与并发数的区分,解释有限缓冲和处理线程不能直接等同下游QPS。
- 国内:腾讯云:RocketMQ限流:采用快速失败、客户端退避和消费配额的处理方式,区分队列服务配额与短信出口限流。
- 国内:腾讯云:RocketMQ配额与限制:核对消息保留期、延时与吞吐受配置限制,说明队列不能无限缓冲。
- 国内:阿里云:消息幂等:采用业务唯一标识处理重复发送与重复投递,区分消息标识和业务动作。
- 国外:Apache RocketMQ:Consumer Types:核对
SimpleConsumer的主动获取/确认,以及PushConsumer回调完成前不能提前确认的契约。 - 国外:Apache RocketMQ:Consumption Retry:核对持续限流不适合靠失败重投控制,应延迟获取消息。
相关面经
1 篇阿里巴巴
AI 全栈开发工程师怎么限流?假设短信平台受不了这么大的量,希望按恒定速率,比如只能承受 100 QPS,你消息生产速度是 1 万 QPS,中间怎么做限量?(追问)怎么跟 MQ 的消费结合起来?消费入口获取令牌获取不到会怎样处理?完整流程是什么?(追问)获取不到就把消息丢回消息队列?这样不会频繁丢吗?不会给 broker 带来很大压力吗?(追问)你说的都是推的模式,MQ 如果是拉的模式,你有什么别的思路吗?
