
1) 【一句话结论】采用消息队列(如RocketMQ/Kafka)作为核心传输层,结合客户端本地缓存(内存+Redis)实现消息去重,网络波动时通过指数退避策略重试,确保百万级用户下消息不丢失、不重复且低延迟。
2) 【原理/概念讲解】
老师:咱们先讲核心组件的作用。消息队列是异步通信的“快递站”,客户端发消息给队列(寄件),服务端从队列取(取件),即使队列断网,寄件也不会丢失——这就是持久化存储的作用,保证消息不丢失。
类比:就像快递公司,寄件人把包裹寄到快递站,快递站即使断网,包裹也不会丢,等恢复后继续派送。
接下来是缓存,客户端发送消息后,先存到本地缓存(如Redis),标记为“未确认”,服务端处理前检查缓存,避免重复处理——这就是去重。
去重逻辑:用消息唯一ID(如UUID)或消息键(key),客户端发送前先查缓存,有则跳过,无则发送。
网络波动重试:客户端发送失败后,按指数退避策略重试(如第一次1秒,第二次2秒,第三次4秒...),避免频繁请求压垮服务器,同时保证重试成功率。
3) 【对比与适用场景】
| 模块 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 消息队列(如RocketMQ) | 分布式消息中间件,支持持久化、顺序、事务 | 高吞吐、持久化、支持重试、事务 | 核心传输,保证消息不丢失,处理网络波动 | 需集群部署,消息延迟毫秒级 |
| 本地缓存(如Redis) | 客户端本地存储未确认消息 | 低延迟、内存访问快 | 存储临时未确认消息,去重 | 需内存管理,避免内存泄漏 |
4) 【示例】
伪代码(客户端发送消息):
def send_message(message_id, content):
# 1. 本地缓存去重(Redis)
if redis.exists(f"msg:{message_id}"):
return "duplicate"
# 2. 发送消息到队列(RocketMQ)
mq_producer.send(message_id, content)
# 3. 存储到本地缓存(标记为未确认)
redis.set(f"msg:{message_id}", "pending")
return "sent"
伪代码(服务端处理消息):
def process_message(message_id, content):
# 1. 检查本地缓存(标记为已处理)
if redis.exists(f"msg:{message_id}"):
redis.set(f"msg:{message_id}", "processed")
process_content(content) # 处理消息(如更新UI)
return "processed"
else:
# 从队列取消息
msg = mq_consumer.consume(message_id)
process_content(msg)
redis.set(f"msg:{message_id}", "processed")
return "processed"
5) 【面试口播版答案】
面试官您好,针对百万级用户低延迟消息推送系统,核心思路是用消息队列(如RocketMQ)做可靠传输,结合客户端本地缓存去重,网络波动时用指数退避重试。
具体来说,消息从客户端发送到队列后,服务端通过消费队列处理,客户端本地缓存存储未确认消息,避免重复处理。去重通过消息唯一ID(或消息键)在Redis中检查,确保同一消息只处理一次。网络波动时,客户端根据指数退避策略重试(如第一次1秒,第二次2秒...),避免频繁请求压垮服务器。这样能保证消息不丢失、不重复,同时低延迟。
6) 【追问清单】
7) 【常见坑/雷区】