微信级别的聊天 App 系统设计:从容应对 亿级日活的架构之道

“何设计一个类似微信的聊天 App?” 微信作为一个支撑 10 亿+ 用户、日活数亿的超级 App,其架构蕴含的智慧值得每个工程师深入学习。

引言:聊天 App 的复杂度超乎想象

很多人觉得聊天 App 很简单:”不就是发消息吗?”

但当你真正要设计一个日活数亿、消息必达、功能丰富的聊天系统时,会发现它可能是你接触过最复杂的系统之一:

  • 实时性:消息延迟要在 200ms 以内
  • 可靠性:消息不能丢、不能重复
  • 高并发:同时在线用户数千万
  • 功能丰富:文字、图片、语音、视频、表情、位置、红包、小程序…
  • 跨设备:手机、平板、电脑、Web 多端同步
  • 全球化:全球部署、低延迟访问

这已经不是一个”小而美”的系统,而是一个超级分布式系统

一、需求分析:我们要设计什么?

1.1 核心功能

功能 描述 技术难点
1v1 聊天 私聊、消息必达 实时推送、离线消息
群聊 多人聊天、满 500 人 Fan-out 优化
消息类型 文字/图片/语音/视频/表情 媒体存储
已读回执 显示”已读” 状态同步
在线状态 显示”在线/离线” 心跳管理
消息撤回 撤回消息 分布式事务
朋友圈/状态 类似微信朋友圈 Feed 流设计
语音/视频通话 实时音视频 WebRTC
消息搜索 搜索聊天记录 搜索引擎

1.2 非功能性需求

flowchart TB
    subgraph NFR["非功能性需求"]
        Perf["性能
消息延迟 < 200ms"] Reliability["可靠性
消息不丢不重"] Scalability["扩展性
支持亿级用户"] Availability["可用性
99.99% SLA"] Security["安全性
端到端加密"] end
  • 消息延迟:端到端延迟 < 200ms(同一地域)
  • 消息必达:离线消息也要送达,消息不丢失
  • 消息不重复:避免重复展示
  • 可用性:99.99% SLA,全年故障时间 < 53 分钟
  • 扩展性:支持从 1 万到 10 亿用户
  • 安全性:端到端加密(E2EE)

二、整体架构设计

2.1 架构总览

flowchart TB
    subgraph Client["客户端"]
        App["微信/QQ
移动端"] PC["桌面客户端"] Web["Web 版本"] end subgraph Gateway["接入层"] LB["负载均衡
L4/L7"] WSG["WebSocket
Gateway"] APIG["API
Gateway"] end subgraph Service["服务层"] Msg["消息服务"] Auth["认证服务"] Relation["关系服务"] Profile["用户服务"] Push["推送服务"] File["文件服务"] end subgraph Storage["存储层"] Redis["Redis Cluster
缓存/消息队列"] MySQL["MySQL Cluster
消息/用户"] HBase["HBase
历史消息"] ES["Elasticsearch
搜索"] OSS["对象存储
图片/视频"] end subgraph RTC_Sub["实时通信"] RTC["音视频服务
WebRTC"] end App & PC & Web --> LB LB --> WSG & APIG WSG & APIG --> Msg & Auth & Relation & Profile & Push & File Msg & Auth & Relation & Push --> Redis Msg & Auth & Relation & Profile --> MySQL Msg --> HBase Msg --> ES File --> OSS App & PC --> RTC

2.2 分层设计

flowchart TD
    subgraph ClientLayer ["客户端层"]
        direction LR
        Client["iOS / Android / Windows / Mac / Web"]
    end

    subgraph GatewayLayer ["接入层"]
        direction LR
        LB["负载均衡器 (L4/L7)"]
        WSG["WebSocket Gateway"]
        APIG["API Gateway"]
    end

    subgraph ServiceLayer ["业务层"]
        direction TB
        subgraph ServiceRow1 [" "]
            direction LR
            Msg["消息服务"]
            User["用户服务"]
            Relation["关系服务"]
            Push["推送服务"]
        end
        subgraph ServiceRow2 [" "]
            direction LR
            Group["群服务"]
            File["文件服务"]
            Call["通话服务"]
            Moments["朋友圈"]
        end
    end

    subgraph StorageLayer ["存储层"]
        direction TB
        subgraph StorageRow1 [" "]
            direction LR
            Redis["Redis (缓存/消息队列)"]
            MySQL["MySQL (业务数据)"]
            HBase["HBase (历史消息)"]
            ES["ES (搜索)"]
        end
        OSS["对象存储 (OSS/S3)
图片/语音/视频/文件"] end ClientLayer --> GatewayLayer GatewayLayer --> ServiceLayer ServiceLayer --> StorageLayer style ServiceRow1 fill:none,stroke:none style ServiceRow2 fill:none,stroke:none style StorageRow1 fill:none,stroke:none

三、核心模块设计

3.1 长连接管理:WebSocket Gateway

聊天系统的核心是长连接——建立一次 TCP 连接,双方可以随时互相发消息。

flowchart LR
    subgraph Connection["长连接生命周期"]
        Connect["建立连接
WebSocket"] Auth["认证
Token 验证"] Register["注册到
路由中心"] Heartbeat["心跳保活
30秒/次"] Message["消息收发"] Disconnect["断开连接
清理状态"] end

3.2 消息模型设计

1
2
3
4
5
6
7
8
9
10
11
// 消息模型
class Message {
Long msgId; // 消息唯一 ID (snowflake)
Long senderId; // 发送者 ID
Long receiverId; // 接收者 ID (单人/群 ID)
Integer type; // 消息类型 (1:文本 2:图片 3:语音 4:视频...)
String content; // 消息内容
Long timestamp; // 发送时间
Integer status; // 消息状态 (0:发送中 1:已发送 2:已送达 3:已读)
String msgKey; // 幂等 Key (senderId + receiverId + timestamp)
}

消息 ID 生成:使用 Snowflake 算法

1
2
3
4
5
6
7
8
# Snowflake: 64 位 ID = 1位符号 + 41位时间戳 + 10位机器ID + 12位序列号
def generate_msg_id():
timestamp = int(time.time() * 1000) - 1609459200000 # 2021-01-01
machine_id = get_machine_id()
sequence = get_sequence()

msg_id = (timestamp << 22) | (machine_id << 12) | sequence
return msg_id

3.3 消息发送流程

sequenceDiagram
    participant UserA as 用户 A
    participant GW as WebSocket Gateway
    participant MQ as 消息队列
    participant Msg as 消息服务
    participant DB as 数据库
    participant Redis as Redis
    participant GWB as Gateway B
    participant UserB as 用户 B

    UserA->>GW: 1. 发送消息
    GW->>MQ: 2. 写入消息队列
    MQ->>Msg: 3. 异步消费
    Msg->>DB: 4. 持久化消息
    DB-->>Msg: 5. 返回 msgId
    Msg->>Redis: 6. 更新消息索引
    Msg-->>GW: 7. 返回发送成功
    
    GW->>Redis: 8. 查询用户 B 的 Gateway 地址
    Redis-->>GW: 9. 返回 gateway_b
    
    GW->>GWB: 10. 推送消息给用户 B
    GWB->>UserB: 11. 实时送达
    
    UserB->>GWB: 12. 发送已读回执
    GWB->>DB: 13. 更新消息状态为"已读"
    GWB->>GW: 14. 推送已读状态给 A
    GW->>UserA: 15. 显示"已读"

3.4 消息存储设计

3.4.1 分表策略

1
2
3
4
5
6
7
8
9
10
-- 消息表按 receiver_id 分片
CREATE TABLE messages_00 (
id BIGINT PRIMARY KEY,
receiver_id BIGINT NOT NULL,
sender_id BIGINT NOT NULL,
type INT,
content TEXT,
created_at BIGINT,
INDEX idx_receiver_created (receiver_id, created_at)
) PARTITION BY HASH(receiver_id) PARTITIONS 256;

分片策略

  • receiver_id 哈希分片,保证同一用户的消息在同一分片
  • 按月/按年归档,历史消息迁移到冷存储

3.4.2 多层存储架构

flowchart TB
    subgraph Storage["消息存储层级"]
        Hot["热数据
Redis
最近 7 天"] Warm["温数据
MySQL
7-90 天"] Cold["冷数据
HBase/OSS
90 天以上"] end Hot -->|"访问"| Warm Warm -->|"归档"| Cold

四、群聊系统设计

4.1 群消息的挑战

群聊和 1v1 聊天完全不同:

场景 1v1 聊天 群聊 (500人)
消息分发 1 人 499 人
Fan-out 简单 复杂
离线处理 1 人 499 人

4.2 群消息的 Fan-out 策略

flowchart TB
    subgraph Fanout["群消息分发"]
        Input["用户 A 发送群消息"]
        
        subgraph Sync["同步 Fan-out (小群 <100人)"]
            S1["查询成员列表"]
            S2["循环推送每个人"]
            S3["完成"]
        end
        
        subgraph Async["异步 Fan-out (大群 >100人)"]
            A1["写入消息队列"]
            A2["Fan-out Service 消费"]
            A3["并行推送给成员"]
            A4["完成"]
        end
        
        Input --> S1 & A1
    end

优化策略

1
2
3
4
5
6
7
8
9
10
11
12
13
# 群消息分发伪代码
async def send_group_message(group_id, sender_id, message):
group = get_group(group_id)
member_count = len(group.members)

if member_count < 100:
# 小群:同步 Fan-out
for member_id in group.members:
await push_to_user(member_id, message)
else:
# 大群:异步 Fan-out
await mq.publish(f"group_msg_{group_id}", message)
# Fan-out Service 异步处理

4.3 群消息的幂等性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 群消息幂等 Key
def generate_group_msg_key(group_id, sender_id, client_msg_id):
return f"group_msg:{group_id}:{sender_id}:{client_msg_id}"

# 消费消息时检查
async def consume_group_message(message):
key = generate_group_msg_key(message.group_id, message.sender_id, message.client_msg_id)

# 检查是否已处理
if await redis.exists(key):
return # 重复消息,直接丢弃

# 处理消息
await process_message(message)

# 标记已处理(设置过期时间)
await redis.setex(key, 86400, "1") # 24 小时过期

五、在线状态与消息推送

5.1 在线状态管理

flowchart TB
    subgraph Online["在线状态流程"]
        Connect["建立连接"] --> Heartbeat["心跳
30秒"] Heartbeat --> Update["更新 Redis"] Update --> Check{"超时?"} Check --"是" --> Offline["标记离线"] Check --"否" --> Continue["保持在线"] end

5.2 推送服务架构

flowchart LR
    subgraph Push["消息推送"]
        Msg["消息服务"] --> MQ["消息队列"]
        MQ --> PushService["推送服务"]
        
        PushService --> APNS["APNs
Apple"] PushService --> FCM["FCM
Google"] PushService --> HMS["HMS
华为"] PushService --> JPush["极光/个推
国内"] end

推送策略

状态 推送方式
在线 WebSocket 实时推送
离线 推送系统 (APNs/FCM)
夜间/勿扰 累计通知栏

六、消息漫游与多端同步

6.1 多端同步问题

用户在手机、平板、电脑上登录,消息需要实时同步:

sequenceDiagram
    participant Phone as 手机
    participant PC as 电脑
    participant Server as 服务器
    
    Phone->>Server: 1. 发送消息
    Server->>Server: 2. 持久化
    Server-->>Phone: 3. 确认送达
    Server-->>PC: 4. 推送消息给 PC
    PC->>Server: 5. 获取完整消息
    Server-->>PC: 6. 返回消息内容

6.2 消息漫游实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 消息漫游服务
class MessageRoamingService:
def __init__(self, cache, db):
self.cache = cache
self.db = db

async def get_messages(self, user_id, device_id, limit=50):
# 1. 先从缓存获取最近消息
cached = await self.cache.get(f"roaming:{user_id}:{device_id}")
if cached:
return cached

# 2. 缓存 miss,从数据库获取
messages = await self.db.query("""
SELECT * FROM messages
WHERE receiver_id = %s
ORDER BY created_at DESC
LIMIT %s
""", user_id, limit)

# 3. 写入缓存
await self.cache.set(f"roaming:{user_id}:{device_id}", messages, expire=3600)

return messages

七、安全性设计

7.1 端到端加密 (E2EE)

flowchart TB
    subgraph E2EE["端到端加密"]
        KeyGen["密钥生成
每个设备独立密钥对"] KeyExchange["密钥交换
Signal Protocol"] Encrypt["消息加密
发送前加密"] Decrypt["消息解密
接收后解密"] end

Signal Protocol 的核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 简化的 E2EE 流程
class E2EEncryption:
def __init__(self):
self.key_pair = generate_key_pair() # Curve25519

def encrypt(self, message, recipient_public_key):
# 生成临时密钥对
ephemeral_key = generate_key_pair()

# ECDH 密钥协商
shared_secret = ecdh(
ephemeral_key.private,
recipient_public_key
)

# AES-GCM 加密
ciphertext = aes_encrypt(shared_secret, message)

return {
'ephemeral_public': ephemeral_key.public,
'ciphertext': ciphertext
}

7.2 传输安全

  • TLS 1.3:全程 HTTPS/WSS 加密
  • 证书固定 (Certificate Pinning):防止中间人攻击
  • 设备绑定:敏感操作需要设备验证

八、搜索功能设计

8.1 搜索架构

flowchart TB
    subgraph Search["搜索服务"]
        Index["索引构建"]
        Query["查询服务"]
    end
    
    subgraph Engine["搜索引擎"]
        ES["Elasticsearch"]
    end
    
    subgraph Storage["存储"]
        DB["消息数据库"]
    end
    
    DB -->|"增量同步"| Index
    Index --> ES
    Query --> ES

8.2 搜索优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 搜索服务
class SearchService:
def __init__(self, es_client):
self.es = es_client

async def search_messages(self, user_id, keyword, limit=20):
# 搜索当前用户有权限的消息
result = await self.es.search(
index="messages",
body={
"query": {
"bool": {
"must": [
{"term": {"keyword": keyword}},
{"term": {"user_id": user_id}} # 权限过滤
]
}
},
"highlight": {
"fields": {"content": {}}
},
"size": limit
}
)

return self.format_result(result)

九、扩展性与高可用

9.1 多 Region 部署

flowchart TB
    subgraph Global["全球部署"]
        subgraph CN["中国区"]
            CN_GW["Gateway"]
            CN_Service["服务"]
            CN_DB["数据库"]
        end
        
        subgraph US["北美区"]
            US_GW["Gateway"]
            US_Service["服务"]
            US_DB["数据库"]
        end
        
        subgraph EU["欧洲区"]
            EU_GW["Gateway"]
            EU_Service["服务"]
            EU_DB["数据库"]
        end
    end
    
    CN_GW <-->|"专线同步"| US_GW
    US_GW <-->|"专线同步"| EU_GW

9.2 容灾与故障转移

组件 容灾策略
Gateway 多集群部署,Load Balancer 切换
消息服务 主备切换,消息队列持久化
数据库 主从复制,跨机房部署
缓存 Redis Cluster,自动故障转移

十、总结

10.1 核心设计原则

  1. 长连接 + 短轮询结合:WebSocket 保持实时性,HTTP 处理非实时请求
  2. 消息队列解耦:削峰填谷,保证系统稳定性
  3. 多层存储架构:热/温/冷数据分层,平衡性能与成本
  4. 灰度发布:新功能先在小范围验证
  5. 监控告警:全链路监控,问题早发现

10.2 关键技术选型

模块 技术选型 理由
长连接 WebSocket 双向通信,低延迟
消息队列 Kafka 高吞吐,持久化
缓存 Redis Cluster 高性能,丰富数据结构
消息存储 MySQL + HBase 事务 + 大数据
搜索 Elasticsearch 全文搜索
对象存储 S3/OSS 海量文件存储

10.3 微信的特殊优化

彩蛋:微信的哪些”神优化”?

  1. 省电模式:智能心跳,节省电量
  2. 弱网优化:消息聚合发送,减少请求
  3. 本地缓存:减少服务器请求
  4. 增量同步:只同步新增消息
  5. 通道复用:复用 TCP 连接

参考资料