
1) 【一句话结论】
采用分布式消息队列(Kafka)作为消息中转,结合Redis缓存实时消息状态与MySQL持久化历史消息,通过用户ID到服务器的映射实现高效路由,并借助集群、备份方案保障高可用与容灾,确保百万级并发下的实时性与可靠性。
2) 【原理/概念讲解】
老师:设计百万级实时消息系统,核心是解决“消息路由、存储、同步、高可用、容灾”这几个关键问题。首先看消息路由:用户ID(如“user123”)需要映射到具体服务器(如Server1),避免单点压力。这里用一致性哈希,但为了防热点,引入虚拟节点(比如把用户ID哈希后,每个物理节点对应多个虚拟节点,分散负载)。然后是消息存储:分为“实时消息”和“历史消息”。实时消息需要快速推送,所以用Redis(内存缓存,支持发布/订阅,比如消息ID、内容、发送时间)作为缓存层,同时通过Kafka持久化(防止消息丢失,比如设置acks=all,确保至少一次投递)。历史消息则存入MySQL(按时间分表,比如按月分区,支持历史查询)。接下来是消息同步:用户A发消息给B,服务器将消息写入Kafka主题(如“userA_to_userB”),消息内容为JSON(包含发送者、接收者、内容、时间)。Kafka消费者(部署在目标服务器)消费消息后,将消息存入Redis(比如用户B的会话缓存),然后通过WebSocket长连接推送给B的客户端,保证实时性。高可用方面,服务器部署集群(多台服务器),消息路由用Nginx+负载均衡(比如轮询或加权轮询),数据库做主从复制(主库写,从库读,提升读写分离),Redis用集群模式(避免单点故障)。容灾则是数据备份,比如MySQL异地备份(RDS异地灾备),Kafka日志备份(多副本存储),服务器故障时自动切换到备用节点(比如通过ZooKeeper或Consul管理节点状态)。
3) 【对比与适用场景】
| 组件 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Kafka | 分布式消息队列 | 高吞吐、持久化、可扩展、支持事务 | 实时消息、日志处理、解耦系统 | 需要持久化磁盘,启动慢,消息延迟较高 |
| Redis | 内存数据库 | 高速缓存、支持发布/订阅、事务 | 实时消息状态、会话管理 | 依赖内存,持久化成本高,单点故障风险 |
| MySQL | 关系型数据库 | ACID、事务支持、结构化存储 | 历史消息持久化、数据持久化 | 扩展性差(写入慢),需分表分库 |
| Nginx | 负载均衡器 | 高性能、灵活配置 | 消息路由、负载均衡 | 需要合理配置负载策略(如虚拟节点映射) |
4) 【示例】
伪代码示例(用户A发送消息给用户B):
// 用户A调用API
POST /send?to=userIdB&msg=hello
// 服务器处理流程
1. 接收请求,解析用户A和用户B的ID。
2. 通过一致性哈希+虚拟节点,将用户A的路由到Server1(消息路由)。
3. 将消息写入Kafka主题(如userA_to_userB),消息内容为JSON:{"sender": "userA", "receiver": "userB", "content": "hello", "time": 1678888888}。
4. Server1消费Kafka消息,将消息存入Redis(key为用户B的会话ID,如"session_userB",value为消息内容)。
5. 通过WebSocket连接推送消息给用户B的客户端。
6. 同时,将消息写入MySQL历史表(表history,字段:sender, receiver, content, time, create_time,按月分区)。
5) 【面试口播版答案】
“设计百万级实时消息系统,核心思路是分层架构:消息路由用用户ID到服务器的映射,实时消息用Kafka中转+Redis缓存,历史消息存MySQL。高可用通过集群,容灾用备份。比如用户A发消息给B,先路由到A的服务器,写入Kafka,再推给B的客户端,同时存历史。具体来说,用户发送消息时,服务器将消息写入Kafka,通过负载均衡路由到目标服务器,服务器消费后用WebSocket推送,同时持久化历史。高可用方面,服务器集群+数据库主从,Redis集群,避免单点故障。容灾则通过数据备份,比如MySQL异地备份,Kafka日志备份,确保故障后能快速恢复。另外,消息丢失通过Kafka的acks=all保障,路由热点用虚拟节点分散,历史查询按时间分表加索引,提升效率。”
6) 【追问清单】
7) 【常见坑/雷区】