IM入门之:消息可靠收发的实现思路

141 阅读4分钟

IM系统消息收发的技术实现

在前文中,我们从用户和开发者视角探讨了IM系统的核心要素。本文将进一步从技术实现层面,解析如何为现有系统快速集成实时消息能力。我们将聚焦三个关键模块:消息存储体系高效收发通道实时未读系统,并通过架构设计实例揭示技术细节。


一、消息存储体系设计:关系型与扩展策略

1.1 核心数据模型设计

以点对点消息为例,采用三表分离结构实现读写优化:

(1)消息内容表(msg_content)

CREATE TABLE msg_content (
  msg_id BIGINT PRIMARY KEY,  -- 雪花算法生成
  content TEXT,               -- 加密存储
  msg_type TINYINT,           -- 文本(1)/图片(2)/文件(3)
  created_at DATETIME(3)      -- 精确到毫秒
);

(2)消息索引表(msg_index)

CREATE TABLE msg_index (
  user_id BIGINT,             -- 用户ID
  peer_id BIGINT,             -- 对方ID  
  msg_id BIGINT,              -- 关联内容表
  direction TINYINT,          -- 0发件/1收件
  PRIMARY KEY(user_id, peer_id, msg_id)
) PARTITION BY HASH(user_id); -- 水平分表

(3)联系人表(recent_contact)

CREATE TABLE recent_contact (
  user_id BIGINT,
  peer_id BIGINT,
  last_msg_id BIGINT,         -- 最新消息ID
  unread_count INT DEFAULT 0, -- 会话未读数
  PRIMARY KEY(user_id, peer_id)
) ENGINE=InnoDB;

设计要点:通过索引表与内容表分离,实现读写分离——高频的索引查询与低频的内容访问解耦。联系人表采用覆盖索引设计,避免N+1查询问题。

1.2 存储架构扩展策略

当单表数据量超过500万时,建议采用:

  • 时序分库:按时间片划分历史库(3月前)与热库
  • NoSQL补偿:将最近7天消息缓存至Redis SortedSet
# Redis存储示例
ZADD user:1000:peer:2000 1625000000 "msg_001|你好"
ZREVRANGE user:1000:peer:2000 0 20 WITHSCORES

二、消息收发通道:多协议融合架构

2.1 发送通道选型策略

协议类型适用场景QPS支撑时延
HTTP/1.1低频消息场景1k100-300ms
HTTP/2移动端混合场景5k50-100ms
TCP长连接高频实时场景10k+<50ms

最佳实践:采用协议自适应策略,客户端根据网络质量动态切换协议版本。

2.2 接收通道可靠性设计

核心组件

  1. Connection Manager:维护百万级长连接,使用Netty实现IO多路复用
  2. ACK确认机制:三级确认体系(客户端ACK→服务端ACK→持久化ACK)
  3. 离线消息队列:Redis Stream实现多终端离线消息堆积
// 伪代码示例:消息投递流程
public void pushMessage(Message msg) {
    if (isOnline(msg.to)) {
        channel.writeAndFlush(msg); // 实时推送
        waitForAck(msg.id, 3000);   // 等待3秒ACK
    } else {
        redis.xadd("offline:"+msg.to, msg); // 离线存储
        triggerPushNotification(msg); // 触发系统通知
    }
}

第三方推送补偿

  • iOS:APNs(需处理证书轮换)
  • 安卓:小米/华为厂商通道+个推补偿
  • Web:Service Worker + WebPush

三、未读数系统:分布式一致性实践

3.1 存储模型设计

CREATE TABLE unread_counter (
  user_id BIGINT PRIMARY KEY,
  total INT DEFAULT 0,          -- 总未读
  version BIGINT                -- 乐观锁版本
);

CREATE TABLE session_unread (
  user_id BIGINT,
  peer_id BIGINT,
  count INT DEFAULT 0,
  PRIMARY KEY(user_id, peer_id)
) ENGINE=InnoDB;

3.2 并发控制方案

采用Redis原子操作+MySQL异步持久化策略:

def incr_unread(user_id, peer_id):
    # Redis原子操作
    redis.hincrby(f"unread:{user_id}", "total", 1)
    redis.hincrby(f"unread:{user_id}", f"peer:{peer_id}", 1)
    
    # 异步落库
    mq.send({
        "op": "incr",
        "user": user_id,
        "peer": peer_id
    })

一致性保障

  1. 写操作:优先更新Redis,保证实时性
  2. 读操作:缓存未命中时穿透查询MySQL
  3. 定时任务:每小时同步Redis与MySQL数据
  4. 异常恢复:启动时重建Redis未读缓存

四、性能优化关键指标

模块关键指标目标值
消息存储写入延迟<10ms
长连接网关单节点连接数>50万
消息投递端到端时延(在线)<200ms
未读数更新更新到可见延迟<100ms
离线消息最大堆积时间<5秒

五、典型部署架构

  Client Apps
      │
      ├──► HTTP/2 API Gateway ──► Kafka ──► Message Processor
      │                                │
      └──► TCP Long Connection ────────┘
                     │
                     ▼
           Cluster Manager (ETCD)
                     │
                     ▼
      ┌──────────┬──────────┐
      │          │          │
   Redis Cluster  MySQL Sharding  Push Service

组件说明

  • Kafka:削峰填谷,处理突发流量
  • ETCD:管理长连接集群状态
  • Redis:存储在线状态、未读数、最近消息
  • Sharding Proxy:实现自动分库分表

演进方向建议

  1. 消息协议优化:采用Protobuf二进制协议,相比JSON节省40%流量
  2. 边缘计算:在CDN节点部署边缘IM网关,降低端到端延迟
  3. AI压缩:对图片/视频消息进行智能压缩,节省存储成本
  4. 区块链存证:对重要消息进行区块链指纹存证,满足合规要求

通过以上架构设计,可在3个月内为日均千万级消息的系统构建高可用IM模块,实现消息到达率99.99%,系统可用性达到4个9。后续可结合业务需求,逐步扩展群聊、消息撤回等进阶功能。

最后

欢迎关注gzh:加瓦点灯, 每天推送干货知识!