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

设计一个支持百万级用户同时在线的PC客户端消息系统,需要考虑哪些技术选型?请从客户端消息推送、服务器端消息队列、消息存储、客户端离线存储等方面分析,并说明如何保证消息的实时性和可靠性。

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

答案

1) 【一句话结论】设计百万级用户消息系统,需采用客户端长连接(WebSocket)+服务器端消息队列(如Kafka)+分布式存储(Redis缓存+关系型数据库持久化),通过消息队列解耦、缓存保证实时性,本地存储保证离线可靠性,结合消息确认机制确保可靠性。

2) 【原理/概念讲解】
要支撑百万级用户同时在线,消息系统需解决实时推送、高并发处理、离线消息和可靠性四大问题:

  • 客户端消息推送:采用长连接(如WebSocket),避免频繁建立/断开连接,支持全双工通信,实现低延迟实时推送(类比:快递中转站,客户端是收件人,服务器是中转站,消息先到队列再由客户端拉取,避免直接通信压力)。
  • 服务器端消息队列:作为消息缓冲与分发中心,解耦客户端与服务器,处理高并发消息(如Kafka)。消息先写入队列,再由消费者(如消息服务)分发,确保系统解耦且能应对突发流量。
  • 消息存储:分两层设计——**Redis(缓存)**用于实时消息(低延迟,内存数据库,适用于高频访问);**关系型数据库(如MySQL)**用于持久化历史消息(支持复杂查询,数据持久化)。
  • 客户端离线存储:用户离线时,消息存储于本地数据库(如SQLite),在线后通过同步机制推送到服务器,再转发给其他用户。

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) 【追问清单】

  • 问:如何处理消息去重?答:通过消息的唯一ID(如消息ID或时间戳+内容哈希),客户端收到消息后检查本地缓存或数据库,若已存在则跳过,避免重复显示。
  • 问:消息延迟如何控制?答:消息队列支持延迟队列或重试机制,结合消息优先级(如高优先级消息优先处理),确保关键消息及时送达,同时控制延迟。
  • 问:系统高可用怎么保证?答:消息队列集群部署(多节点),服务器多节点负载均衡,客户端连接通过负载均衡器分发,存储分库分表,确保单点故障不影响整体服务。
  • 问:离线消息同步策略?答:用户在线时,从本地SQLite读取离线消息,通过消息队列发送给服务器,服务器再推送给其他用户;同时,服务器将已发送的消息标记为已同步,避免重复同步。
  • 问:如何保证消息的最终一致性?答:结合消息确认(客户端发送ACK)和补偿机制,若消息发送后未收到ACK,服务器重试发送;若重试失败,记录错误并通知运维,确保消息最终被处理。

7) 【常见坑/雷区】

  • 只考虑实时性,忽略离线消息处理,导致用户离线时消息丢失,影响用户体验。
  • 存储方案单一,比如只用缓存(Redis),没有持久化(关系型数据库),导致断电或客户端重启后数据丢失。
  • 消息队列选择不当,比如用RabbitMQ处理高吞吐(如百万级消息/秒)时,性能不足,导致消息积压;或Kafka在低延迟场景下(如毫秒级)延迟较高,影响实时性。
  • 客户端连接管理不当,比如长连接断开后重连逻辑复杂(如超时重连、连接池管理),导致用户重新登录后消息延迟或丢失。
  • 消息确认机制缺失,比如服务器发送消息后不等待客户端ACK,导致消息丢失或重复,影响可靠性。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1