51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个支持百万级用户同时在线的实时聊天系统,需要考虑网络延迟、消息丢失、消息顺序保证,请描述客户端的架构设计,包括消息队列、缓存、网络协议选择等。

Tencent软件开发-游戏客户端开发方向难度:中等

答案

1) 【一句话结论】
采用“WebSocket持久连接+分布式消息队列(如Kafka,按用户ID分区+日志持久化)+分布式缓存(如Redis主从复制+RDB/AOF持久化)+消息确认与指数退避重试机制”的架构,通过消息队列削峰填谷、缓存保障状态同步与重传,结合WebSocket低延迟通信,尽量保证百万级用户下的低延迟、消息不丢失且顺序正确。

2) 【原理/概念讲解】
老师口吻解释:

  • WebSocket:基于TCP的全双工通信协议,建立持久连接,减少TCP握手开销(类比“不挂断的电话”,保持连接随时通信,无需每次拨号)。
  • 消息队列(如Kafka):分布式消息系统,用于解耦消息发送与接收。当用户激增时,队列缓冲消息,避免服务端瞬间压垮。关键配置:
    • 持久化:开启log.retention.hours=48,确保消息不因服务重启丢失;
    • 分区策略:按用户ID分区(如chat_messages主题按目标用户ID分区),保证同一用户消息顺序(因为同一分区消息按时间顺序写入)。
  • 分布式缓存(如Redis):内存数据库,用于存储消息状态(如已读/未读标记)或消息重传队列。关键配置:
    • 持久化:RDB+AOF,确保缓存数据不丢失;
    • 主从复制:主从集群,确保消息状态同步(主节点写入,从节点同步,避免客户端获取过时状态)。
  • 消息确认与重试:服务端收到消息后发送ACK,若超时未收到则指数退避重试(如第一次1秒,第二次2秒,第三次4秒),确保消息不丢失。

3) 【对比与适用场景】
以消息队列(Kafka vs RocketMQ)和缓存(Redis vs Memcached)为例:

组件定义特性使用场景注意点
消息队列(Kafka)分布式消息系统高吞吐、持久化、支持消费组、按时间/分区顺序写入实时日志、流处理、聊天消息需持久化磁盘,启动慢,需配置日志保留时间
消息队列(RocketMQ)消息中间件高可用、持久化、支持事务消息、顺序消息金融、电商、聊天(事务消息复杂)事务消息实现复杂,需保证消息顺序
缓存(Redis)内存数据库高速读写、支持数据结构(字符串、列表等)、主从复制消息状态(已读标记)、会话、重传队列容量有限,需限流,需持久化
缓存(Memcached)内存缓存速度快、简单、无持久化热点数据缓存重启数据丢失,不适合持久化场景

4) 【示例】
伪代码示例(消息发送与接收流程):

  • 客户端发送消息:
    POST /send_message
    {
      "user_id": "u1",
      "target_id": "u2",
      "content": "hello",
      "timestamp": 1678888888
    }
    
  • 服务端处理:
    1. 将消息写入Kafka主题chat_messages,分区按target_id(如分区0对应u2),保证同一用户消息顺序;
    2. 存入Redis:messages:u2:u1 = "hello"(消息内容,用于重传);
    3. 发送ACK给客户端(如HTTP 200),若超时未收到ACK则指数退避重试;
    4. WebSocket连接的客户端订阅u2消息,服务端从Kafka消费(按分区顺序)并推送给客户端。
  • 客户端接收:通过WebSocket长连接订阅消息,服务端推送后,前端展示。

5) 【面试口播版答案】
面试官您好,设计百万级用户实时聊天系统,核心是解决高并发下的低延迟、消息可靠。首先,客户端用WebSocket建立持久连接,减少TCP握手开销。后端架构分为消息队列(如Kafka)和缓存(Redis)。消息队列用于削峰,比如用户激增时缓冲消息,避免服务端压垮;缓存用于存储消息状态(如已读标记)或重传队列。具体流程:客户端发送消息到服务端,服务端写入Kafka(按目标用户ID分区保证顺序),同时存入Redis,并发送ACK。客户端通过WebSocket订阅目标用户,服务端从Kafka消费并推送给客户端。这样既保证低延迟,又通过队列持久化避免消息丢失,缓存保障消息状态同步。总结来说,通过WebSocket实现实时通信,消息队列处理高并发,缓存保障可靠性,整体架构能支撑百万级用户同时在线。

6) 【追问清单】

  • 问题1:如何保证消息不丢失?
    回答要点:消息队列持久化(Kafka开启日志持久化),结合Redis缓存消息状态,服务端发送ACK,超时重试(指数退避)。
  • 问题2:消息顺序如何保证?
    回答要点:消息队列按用户ID分区,保证同一用户消息顺序;客户端按分区顺序消费。
  • 问题3:网络延迟优化?
    回答要点:WebSocket持久连接,减少握手;缓存本地消息,减少重传。
  • 问题4:扩展性?
    回答要点:消息队列水平扩展(增加分区/实例),缓存集群(Redis分片),WebSocket连接池。
  • 问题5:离线消息处理?
    回答要点:消息队列存储未读消息,用户上线后从队列拉取。

7) 【常见坑/雷区】

  • 误区1:消息队列未持久化,导致消息丢失(如Kafka未开启log.retention.hours)。
  • 误区2:分区策略错误(如按消息ID分区),导致消息顺序乱。
  • 误区3:缓存未主从复制,导致数据不一致(如客户端获取过时消息状态)。
  • 误区4:ACK超时重试策略不当(如线性重试,导致服务端压力过大)。
  • 误区5:绝对化表述(如“确保消息不丢失”),未说明实际概率(如0.1%丢失率)。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1