IM 即时通讯系统中的聊天页面只是最外层的表现。真正需要重点设计的是长连接接入、消息协议、消息可靠性、离线同步、群聊扩散、多端状态一致性、跨节点投递、文件传输、音视频信令以及数据持久化。

系统同时覆盖 Android、iOS、H5、PC 四端,后端架构需要处理的不只是“用户 A 给用户 B 发消息”,还要考虑用户多端在线、弱网重连、消息重复发送、群聊权限、已读未读、消息撤回、钱包流水、朋友圈动态、文件上传、国际化文案等复杂场景。

1. 系统模块拆分

IM 系统可以拆成多个相互协作的模块,每个模块都对应不同的数据模型和业务边界。

模块 主要内容
用户关系 好友、好友申请、删除好友、黑名单、名片分享
会话消息 单聊、群聊、文本、图片、文件、语音、视频、表情
群组管理 群成员、群主、管理员、群公告、禁言、群二维码
消息状态 已读未读、撤回、引用回复、合并转发、离线消息
扩展业务 红包、钱包、收藏、朋友圈、系统通知
实时能力 语音通话、视频通话、多人音视频会议
多端适配 Android、iOS、H5、PC
国际化 i18n 多语言包

从技术角度看,以上功能都依赖几个基础能力:连接管理、消息路由、协议解析、数据持久化、缓存、状态同步和集群转发。

2. 整体架构分层

一个四端 IM 系统可以按职责分为客户端层、接入层、业务层、缓存层和持久层。

客户端层

移动端可以使用 uniapp 覆盖 Android、iOS 和 H5。它主要负责聊天页面、会话列表、朋友圈、扫码、文件选择、图片预览、消息本地缓存、断线重连等能力。

PC 端使用 Vue2 实现,更适合处理桌面端聊天界面,例如左侧会话列表、中间聊天窗口、右侧群资料面板、文件拖拽发送、聊天记录搜索、收藏管理等。

接入层

接入层负责长连接维护、连接鉴权、协议解析、心跳检测和消息转发。

常见接入方式包括:

协议 使用场景
Socket 自定义协议 App 长连接、低冗余消息传输
WebSocket H5、PC 实时通信
HTTP 登录、好友列表、群资料、历史消息、文件上传

Socket 和 WebSocket 负责实时消息,HTTP 负责查询类、管理类和上传类接口。不同协议可以共享后端业务能力,也可以在部署时拆分为独立接入服务。

业务层

业务层由 SpringBoot 实现,主要处理好友、群聊、红包、钱包、朋友圈、收藏、文件、系统通知、i18n 等模块。

通信层只负责收发消息,业务层负责判断消息是否允许发送、发送给哪些用户、是否需要落库、是否需要同步到其他端。

缓存层

Redis 适合处理高频变化的数据,例如:

数据类型 Redis 用途
在线状态 保存用户连接信息
连接路由 记录用户连接在哪个 IM 节点
未读数 按用户和会话维度维护
临时 token 扫码、文件访问、短期授权
分布式锁 红包领取、并发状态控制
热点缓存 群成员、会话信息、用户状态

持久层

MySQL 5.7+ 用于保存结构化数据,例如用户表、好友表、群组表、群成员表、消息表、会话表、文件表、钱包流水表、朋友圈动态表、收藏表等。

3. 消息协议设计

IM 消息协议需要尽量简洁,同时保留扩展能力。文本、图片、文件、表情、自定义表情、红包、名片、位置、引用回复、合并转发等消息,都可以抽象成统一结构。

{
  "cmd": "MSG_SEND",
  "clientMsgId": "c_10001_202606300001",
  "serverMsgId": null,
  "fromUserId": 10001,
  "toId": 20001,
  "conversationId": "single_10001_20001",
  "chatType": "single",
  "msgType": "text",
  "content": {
    "text": "这是一条单聊文本消息"
  },
  "extension": {
    "quoteMsgId": null,
    "atUserIds": []
  },
  "timestamp": 1782777600000
}

字段设计可以按以下方式理解:

字段 说明
cmd 消息命令,例如发送、撤回、已读、心跳
clientMsgId 客户端生成,用于弱网重试和消息去重
serverMsgId 服务端生成,用于消息排序和历史查询
fromUserId 发送方用户 ID
toId 接收方用户 ID 或群 ID
conversationId 会话 ID
chatType 单聊、群聊、系统消息
msgType 文本、图片、文件、红包、名片等
content 消息正文
extension 扩展字段,例如引用回复、@成员

extension 字段很重要,它可以承载非通用信息,避免每新增一种消息形态就修改主协议结构。

4. 长连接管理

IM 系统需要维护用户和连接之间的映射关系。由于一个用户可能同时登录 Android、iOS、H5、PC,在线状态不能简单写成 userId -> channelId

更合理的结构是:

userId device serverId channelId lastActiveTime
10001 android im-node-1 ch-001 1782777600000
10001 pc im-node-2 ch-889 1782777605000

Redis 可以用来维护在线状态和连接路由。

@Service
public class OnlineService {

    private final StringRedisTemplate redisTemplate;

    public OnlineService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void bind(Long userId, String device, String serverId, String channelId) {
        String key = "im:online:" + userId;
        String value = serverId + ":" + channelId;

        redisTemplate.opsForHash().put(key, device, value);
        redisTemplate.opsForHash().put(key, device + ":lastActive", String.valueOf(System.currentTimeMillis()));
        redisTemplate.expire(key, Duration.ofMinutes(10));
    }

    public Map<Object, Object> getConnections(Long userId) {
        return redisTemplate.opsForHash().entries("im:online:" + userId);
    }

    public void refresh(Long userId) {
        redisTemplate.expire("im:online:" + userId, Duration.ofMinutes(10));
    }
}

客户端需要定时发送心跳,服务端收到心跳后刷新 Redis 过期时间。如果超过指定时间没有收到心跳,就可以清理连接状态。

多端在线场景下,服务端投递消息时需要判断是否投递到所有端,还是只投递到当前活跃端。普通聊天消息通常需要多端同步;某些临时状态可以只投递到当前端。

5. 消息发送链路

一条消息从客户端发送到接收方,通常包含以下阶段:

阶段 处理内容
客户端发送 生成 clientMsgId,消息进入发送中状态
接入层解析 校验连接、解析协议、识别命令
业务层校验 判断好友关系、群成员权限、禁言状态
服务端编号 生成 serverMsgId 和发送时间
消息落库 写入消息表
在线投递 查找接收方连接并推送
离线处理 接收方离线时等待后续同步
ACK 返回 通知发送方服务端已接收

ACK 机制是消息可靠性的基础。客户端发送消息后,不能直接认为消息成功,只有收到服务端 ACK 后,才把消息状态从“发送中”更新为“已发送”。

弱网环境下,客户端可能重复发送同一条消息。服务端可以通过 fromUserId + clientMsgId 做幂等处理,避免重复写入消息表。

6. 消息存储设计

IM 消息存储至少要支持离线消息、历史消息和漫游消息。

离线消息

接收方不在线时,消息不能丢失。用户重新上线后,需要根据会话或用户维度同步离线期间产生的消息。

历史消息

用户在聊天窗口向上滚动时,需要分页加载更早的聊天记录。常见查询条件是 conversationId + sendTimeconversationId + msgSeq

漫游消息

用户切换设备后,仍然需要看到之前的消息。例如手机端发送的消息,PC 登录后也应该能同步到对应会话。

消息表可以按以下方式设计:

CREATE TABLE im_message (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    server_msg_id VARCHAR(64) NOT NULL,
    client_msg_id VARCHAR(64) DEFAULT NULL,
    conversation_id VARCHAR(64) NOT NULL,
    from_user_id BIGINT NOT NULL,
    chat_type VARCHAR(20) NOT NULL,
    msg_type VARCHAR(20) NOT NULL,
    content JSON NOT NULL,
    status TINYINT DEFAULT 0,
    send_time BIGINT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_server_msg_id (server_msg_id),
    KEY idx_conversation_time (conversation_id, send_time),
    KEY idx_from_client (from_user_id, client_msg_id)
);

idx_conversation_time 用于会话历史消息分页查询,idx_from_client 用于客户端消息幂等判断。

当消息量持续增长时,需要进一步考虑分表策略。可以按时间、会话 ID、用户 ID 等维度拆分,避免单表数据量过大影响查询性能。

7. 单聊与群聊的技术差异

单聊消息链路相对简单。服务端只需要判断双方关系、黑名单、账号状态和接收方在线情况,然后完成落库与投递。

群聊处理更复杂,主要体现在以下几个方面。

群成员校验

发送群消息前,需要判断用户是否在群内、是否被禁言、群是否有效、是否具备发送当前消息类型的权限。

群消息扩散

群聊消息不能简单理解为“给每个成员复制一条消息”。对于群成员较多的场景,消息正文通常只存一份,成员维度记录未读状态和读取位置。

@成员提醒

当消息中包含 @群成员时,需要在消息扩展字段中保存被 @ 的用户 ID。客户端收到消息后,根据当前用户 ID 判断是否展示提醒。

已读未读

单聊已读可以记录双方最后已读消息 ID。群聊已读需要按成员记录 lastReadMsgIdlastReadSeq,再计算未读数量。

8. 消息撤回、引用回复与合并转发

消息撤回通常不是物理删除,而是修改消息状态,并向相关客户端推送撤回事件。

撤回流程可以拆成以下几步:

步骤 说明
查询消息 判断消息是否存在
身份校验 判断是否为本人消息
时间校验 判断是否超过允许撤回时间
状态更新 修改消息状态为已撤回
事件推送 通知相关在线端刷新消息

引用回复可以通过 quoteMsgId 关联原消息,而不是复制完整原消息内容。客户端展示时读取原消息摘要即可。

合并转发可以设计为一种特殊消息类型。主消息只展示摘要,点击后再加载转发包中的消息列表。这样可以减少主会话中的内容冗余。

9. 红包与钱包的数据边界

红包可以作为一种消息类型展示在聊天窗口中,但红包创建、领取、退款、余额变化不应直接写进普通消息逻辑。

可以按职责拆分为:

模块 职责
IM 消息模块 展示红包消息、推送红包状态
红包模块 创建、领取、过期、退款
钱包模块 余额、冻结、流水、退款入账
风控模块 幂等、并发控制、异常处理

领取红包时,需要处理并发问题。例如多人同时领取同一个红包时,需要保证不会重复领取或超额领取。实现方式可以包括数据库行锁、Redis 分布式锁、队列串行化处理等。

资金类数据应保存完整流水。每一次余额变化都需要对应一条流水记录,便于状态追踪和异常排查。

10. 音视频通话与多人会议

语音通话、视频通话和多人音视频会议一般不会直接通过 IM 长连接传输媒体流。IM 更适合作为信令通道。

常见信令包括:

信令 含义
CALL_INVITE 发起通话邀请
CALL_ACCEPT 接听
CALL_REJECT 拒绝
CALL_CANCEL 取消呼叫
CALL_HANGUP 挂断
MEETING_CREATE 创建会议
MEETING_JOIN 加入会议
MEETING_LEAVE 离开会议

媒体流可以交给 RTC 通道处理,IM 负责通话邀请、接听状态、房间信息、成员变化、挂断通知等控制消息。

这种拆分方式可以减少聊天长连接的传输压力,也便于同时支持单聊音视频、群聊音视频和多人会议场景。

11. 文件、收藏与朋友圈

文件发送通常分成两个阶段。

第一阶段,客户端通过 HTTP 上传文件,服务端返回文件 ID、文件名、大小、类型、访问地址等元数据。

第二阶段,客户端发送一条文件类型 IM 消息,消息体只保存文件元数据,不直接携带文件二进制内容。

收藏功能可以抽象为统一对象:

收藏类型 来源
文本 单聊、群聊
图片 聊天消息、朋友圈
文件 文件消息
语音 语音消息
视频 视频消息、朋友圈
聊天记录 合并转发

朋友圈更接近内容流系统。它可以复用用户体系和好友关系,但不建议与聊天消息共用同一张表。朋友圈需要独立处理动态发布、评论、点赞、图片视频资源、可见范围和内容列表。

12. 多端同步机制

Android、iOS、H5、PC 同时在线时,状态同步是一个重点问题。

常见场景包括:

场景 同步要求
PC 端读过消息 移动端未读数同步减少
App 端撤回消息 H5 和 PC 同步显示撤回状态
H5 端收藏文件 移动端收藏列表同步更新
群资料变更 在线端刷新群信息
用户修改头像昵称 会话列表同步更新

这些行为可以通过状态事件同步。

事件类型 说明
READ_RECEIPT 已读回执
MSG_REVOKE 消息撤回
CONVERSATION_TOP 会话置顶
CONVERSATION_MUTE 会话免打扰
GROUP_UPDATE 群资料变更
FAVORITE_ADD 新增收藏
SYSTEM_NOTICE 系统通知

状态事件可以和普通聊天消息共用长连接通道,但客户端渲染时需要区分普通消息和状态变更。

13. i18n 多语言包设计

四端系统如果分别维护文案,容易出现命名不一致、翻译不一致、状态展示不一致的问题。可以使用统一 key 管理多语言文案。

key 中文 英文
chat.send 发送 Send
chat.revoke 撤回 Revoke
group.notice 群公告 Group Notice
wallet.balance 余额 Balance
message.unread 未读消息 Unread Messages
file.upload 上传文件 Upload File

前端根据当前语言环境加载不同语言包。后端生成系统通知时,也可以根据用户语言偏好返回对应文案。

14. 集群部署与跨节点投递

单机部署时,用户连接、消息处理和投递都在同一节点内完成。集群部署后,用户可能连接在不同 IM 节点上。

例如:

用户 连接节点
用户 A im-node-1
用户 B im-node-2

当用户 A 给用户 B 发送消息时,节点 1 需要知道用户 B 当前在哪个节点,然后将消息转发到节点 2,再由节点 2 投递到用户 B 的连接。

连接路由可以保存在 Redis 中:

userId device serverId channelId
10001 android im-node-1 ch-001
20001 pc im-node-2 ch-889

跨节点投递可以通过 Redis Pub/Sub、消息队列、内部 RPC 等方式实现。

集群环境需要重点处理以下问题:

问题 处理方向
在线状态不准确 心跳刷新、过期清理
节点异常下线 定时修正连接路由
跨节点重复投递 消息 ID 幂等判断
目标用户离线 离线消息兜底
多端连接覆盖 按 userId + device 维护连接
消息顺序错乱 会话维度序号或时间排序

15. API 接口边界

IM 系统通常会提供大量 HTTP API,但并不是所有能力都应该放在长连接里。

API 类型 示例
好友接口 好友申请、同意好友、删除好友、好友列表
群组接口 创建群、加入群、退出群、群成员列表
消息接口 历史消息、离线消息、撤回消息、已读回执
文件接口 上传文件、下载文件、获取文件信息
钱包接口 余额查询、流水查询、红包领取
朋友圈接口 发布动态、评论、点赞、动态列表
收藏接口 添加收藏、删除收藏、收藏列表

HTTP 更适合查询类、管理类、上传类接口。长连接更适合实时消息、状态事件和通知推送。

模块之间也需要保持边界清晰。红包消息可以出现在聊天窗口中,但资金变更应由钱包模块处理;朋友圈可以复用用户关系链,但不应依赖聊天消息表;音视频可以复用 IM 信令,但媒体流不应走 IM 消息通道。

16. 部署与运行关注点

开发环境一般包含以下组件:

组件 说明
JDK 运行 SpringBoot 服务
MySQL 5.7+ 存储用户、消息、群组、关系链等数据
Redis 在线状态、未读数、缓存、锁
uniapp Android、iOS、H5 客户端
Vue2 PC 客户端
WebSocket / Socket 服务 长连接接入

部署阶段需要关注:

方向 说明
SSL/TLS 长连接和接口传输加密
长连接数量 连接数、心跳频率、连接清理
Redis 内存 在线状态、未读数、缓存过期策略
MySQL 索引 消息分页、会话查询、关系链查询
消息表增长 分表、归档、冷热数据拆分
文件存储 上传、下载、访问权限、容量控制
日志采集 连接日志、消息日志、异常日志
接口限流 登录、发消息、上传、扫码等高频接口
节点扩容 接入层横向扩展、跨节点路由
监控指标 在线人数、消息量、延迟、失败率

对于 IM 系统而言,运行阶段需要持续观察连接数、消息发送量、离线消息堆积、接口耗时、Redis 命中率、数据库写入速度、跨节点投递耗时等指标。长连接服务、业务服务、缓存服务和数据库服务之间的状态变化,会直接影响消息实时性和多端同步效果。

官方演示路径——宠友 IM宠友(IM即时通讯)app,支持语音、文件、图片、视频等多种类型消息的发送,安全可靠,交流轻松,私有化部署,快速开发,极简部署,支持群聊管理、语音视频通话...功能https://chongyou.info/1/product/im.html

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐