
1) 【一句话结论】设计百万级用户消息系统,需采用客户端长连接(WebSocket)+服务器端消息队列(如Kafka)+分布式存储(Redis缓存+关系型数据库持久化),通过消息队列解耦、缓存保证实时性,本地存储保证离线可靠性,结合消息确认机制确保可靠性。
2) 【原理/概念讲解】
要支撑百万级用户同时在线,消息系统需解决实时推送、高并发处理、离线消息和可靠性四大问题:
3) 【对比与适用场景】
| 方案 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| WebSocket | 基于HTTP的长连接协议 | 全双工通信,低延迟,支持实时推送 | 实时消息(如聊天、即时通知) | 需要服务器支持,连接断开后需重连逻辑 |
| 消息队列(Kafka) | 分布式消息系统 | 高吞吐、持久化、可扩展、支持流处理 | 大规模消息流,解耦系统(如日志、消息分发) | 需要集群部署,消息持久化成本高,延迟较低但需配置 |
| 消息队列(RabbitMQ) | 企业级消息中间件 | 队列、主题、交换机,支持多种消息模式 | 中等规模系统,需要消息路由 | 部署相对简单,但吞吐量低于Kafka |
| Redis(缓存) | 内存数据库 | 低延迟,支持发布/订阅 | 实时消息缓存(如聊天消息,用户在线时显示) | 适用于小数据量,内存有限,需考虑缓存击穿、雪崩 |
| 关系型数据库(MySQL) | 持久化存储 | 支持事务、复杂查询、数据一致性 | 历史消息存储,用户消息记录 | 写性能较低,适合读多写少场景,需分库分表 |
4) 【示例】
客户端通过WebSocket订阅消息,服务器通过Kafka发送消息,客户端本地存储离线消息(伪代码):
# 客户端:WebSocket连接 + 本地SQLite存储
import websocket
import json
import sqlite3
ws = websocket.WebSocketApp("wss://chat-server.com/ws",
on_message=on_message,
on_open=on_open,
on_close=on_close)
def on_open(ws):
ws.send(json.dumps({"type": "subscribe", "topic": "user:123:messages"}))
def on_message(ws, message):
msg = json.loads(message)
# 检查消息是否已存在(避免重复)
db = sqlite3.connect("offline_messages.db")
cursor = db.cursor()
cursor.execute("SELECT 1 FROM messages WHERE id = ?", (msg['id'],))
if cursor.fetchone() is None:
print(f"收到消息: {msg['content']}")
cursor.execute("INSERT INTO messages (id, content, sender, ts) VALUES (?, ?, ?, ?)",
(msg['id'], msg['content'], msg['sender'], msg['ts']))
db.commit()
ws.run_forever()
# 服务器端:Kafka生产者发送消息
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers='kafka:9092')
producer.send('chat:messages', key=b'user:123', value=json.dumps({
"sender": "user:456",
"content": "你好",
"id": 1,
"ts": "2023-10-27T10:00:00"
}))
producer.flush()
5) 【面试口播版答案】
设计百万级用户消息系统,核心是采用客户端长连接(WebSocket)保证实时推送,服务器端用消息队列(如Kafka)解耦并缓冲消息,存储分两层:Redis缓存实时消息(低延迟),关系型数据库持久化历史。客户端离线时用本地SQLite存储消息,在线后同步。通过消息确认机制(如ACK)确保可靠性,比如消息发送后等待客户端确认,未确认的重试。这样既能保证实时性,又能处理离线场景,应对百万级并发。
6) 【追问清单】
7) 【常见坑/雷区】