海狸IM 2.0 服务端技术解析:go-zero 微服务与消息链路
海狸IM 2.0 服务端技术解析:go-zero 微服务与消息链路
接上一篇《海狸IM 2.0 开放能力说明:OAuth2 接入与群推送机器人》,44 号文讲了六端发版,45 号文讲了 OAuth 与群推送。本篇从 后端工程 切入,把 2.0 服务端拆成几个关键技术点逐一分析:微服务怎么划、消息怎么发、实时怎么推、离线怎么补、开放怎么接。
IM 后端看起来就是「存消息 + 推消息」,真做起来会发现:会话、seq、多端同步、媒体态、MQ 解耦、运营 RPC……每个点都能单独写一篇文章。这篇先讲 总链路,后面系列会拆更细(OAuth 源码 walkthrough、datasync 协议、WS 连接管理等)。
1. 为什么 IM 后端一定要「拆」
很多初学者写 IM,习惯一个 Go 进程里塞:登录、好友、群、聊天、文件、推送。1.x 阶段能跑,用户一多、功能一加就痛苦:
- 改 OAuth 误伤聊天接口
- WS 连接数和 HTTP QPS 抢资源
- 运营后台和用户 API 混路由,安全风险大
- 单点发布,动一行代码全服务重启
海狸 IM 2.0 用 go-zero 按业务域拆成多个 API / RPC 服务,统一 gateway 对外 REST,ws 单独扛长连接。这不是为了「微服务而微服务」,而是 IM 天然有多条链路:
| 链路 | 特点 | 适合独立 |
|---|---|---|
| 聊天读写 | 高 QPS、强一致 seq | chat + datasync |
| 实时推送 | 长连接、内存态 | ws |
| 第三方接入 | OAuth、Webhook、Robot | open + auth |
| 运营管理 | 低 QPS、高权限 | backend |
| 媒体 | 大 payload、对象存储 | file |
二次开发建议:fork 后先认清你要动哪条链路,再进对应 app/ 目录,别在整个 monolith 里 grep。
2. go-zero 工程结构:API 与 RPC 两层
beaver-server 里每个业务域通常是 双份:
app/chat/
├── chat_api/ # 对外 REST,给客户端 / 网关调用
├── chat_rpc/ # 内部 gRPC,给其它服务调用
└── chat_models/ # 数据模型
为什么要分 API 和 RPC?
- chat_api:面向 HTTP,做参数校验、JWT 鉴权、聚合多个 RPC 调用
- chat_rpc:面向内部,
SendMsg、GetSyncMessageMedias等核心逻辑在这里 - 其它服务需要发消息、查会话,直接调 chat_rpc,不必绕 HTTP
go-zero 的 .api 文件定义 REST 路由,.proto 定义 RPC,代码生成 handler / logic 骨架。读海狸源码时,习惯路径是:
handler → logic → rpc client → 对方 rpc logic → model
熟悉这条链,看任何模块都是同一套。
3. 核心模块深度对照
下面按 「你会改到什么」 排序,不是简单目录罗列。
3.1 gateway — 统一 front door
| 职责 | 说明 |
|---|---|
| 路由转发 | 把 /v1/chat/... 转到 chat_api |
| 鉴权 | JWT 校验,未登录拒掉 |
| 跨域 | 选择题;生产环境配合 Nginx |
客户端(desktop / flutter)、beaver-manager、beaver-open 都只认网关地址,不直连 chat_api 内网端口。私有化定制时,改域名、改证书,动 gateway 配置和 Nginx 即可。
3.2 chat — IM 的心脏
app/chat 负责:
- 会话(conversation)与用户会话关系(user_conversation)
- 消息收发、seq 递增、撤回、编辑、已读 seq
- 消息媒体态(语音未听等)
- 通知类消息(入群、加好友等)
2.0 消息类型在 chat_rpc.proto 里定义得很全,例如:
message TextMsg {
string content = 1;
}
message ImageMsg {
string file_url = 1;
int32 width = 2;
int32 height = 3;
}
message VoiceMsg {
string file_url = 1;
int32 duration = 2;
}
设计要点:多媒体统一 file_url,和 2.0 客户端「一个字段走天下」对齐。扩展新消息类型时,先改 proto → 生成代码 → 双端加渲染组件,顺序别反。
3.3 datasync — 离线端的「变更摘要」
纯 WS 推解决不了 离线用户:用户睡觉八小时,上线后不能靠 WS 补历史。
datasync 模块提供 「自某时间点以来,哪些会话的 seq 变了」 这类摘要接口。客户端拿摘要和本地 seq 对比,再调 chat 的 sync 接口 按区间拉消息——这是典型的 「摘要 + 按需拉取」,微信、Telegram 类 IM 都是这个思路,只是字段名不同。
3.4 ws — 长连接与 MQ 消费者
app/ws 不直接写 MySQL,它干两件事:
- 维护用户 WebSocket 连接(内存 / Redis 映射)
- 消费 RocketMQ 消息,推给在线用户
chat 服务发完消息 不直接调 ws HTTP,而是往 MQ 丢 payload,ws 实例消费后 SendMsgToUser。好处:
- chat 和 ws 进程解耦,chat 挂了不影响已有连接(极端情况)
- ws 可多实例水平扩展
- 广播模式:每个 ws 节点都消费,推本机在线连接(代码注释里写明了
广播模式:每个 WS 实例都消费)
MQ 消费者核心逻辑(简化理解):
// chat_api / chat_rpc 发 MQ
payload := map[string]interface{}{
"command": wsCommandConst.CHAT_MESSAGE,
"type": messageType,
"targetId": recipientId,
"body": tableUpdates,
"conversationId": conversationId,
}
RocketMQ.SendMessage(ctx, MqTopicWs, payload)
// ws_api 消费
ws_conn.SendMsgToUser(targetID, command, content)
这条 MQ 链路是读海狸后端最值得花时间的部分之一——搞懂它,就搞懂了「REST 写库 + 异步推 WS」。
3.5 friend / group / user
| 模块 | 2.0 相关能力 |
|---|---|
| friend | 好友关系、申请、黑名单 |
| group | 群成员、权限、群助手机器人创建 |
| user | 资料、设备、用户设置 JSON(PC 快捷键同步) |
发消息前 chat 会 RPC 调 friend / group 校验权限(是否好友、是否在群、是否被禁言等),失败直接拒绝,不写库。
3.6 file — 媒体上传
图片、语音、视频、文件先走 file 服务上传 MinIO(或配置的对象存储),聊天消息里只存 fileUrl。2.0 刻意统一字段,减少 PC 和 Flutter 各写一套 URL 拼接逻辑。
3.7 open + auth — 开放能力
| 模块 | 职责 |
|---|---|
| open_rpc / open_portal | OAuth2 应用、Token、Robot 相关 RPC |
| auth | 登录、JWT、扫码态 |
再次强调:群 推送机器人 走 bot_public + PC 群助手,不是 open 门户配置。45 号文讲产品边界,这里讲代码边界。
3.8 backend — 运营专用
beaver-manager 调 backend_admin 和各 RPC:用户 360、批量封禁、内容审核、在线监控。和普通用户 JWT 权限域隔离。
3.9 call — LiveKit 音视频
2.0 双端音视频基于 LiveKit。call 模块负责房间、Token 等,和 chat 消息链路分开,避免通话信令和聊天 MQ 搅在一起。
4. 一条消息的完整生命周期(2.0 实测链路)
以 用户 A 在 PC 给群友发一条文本 为例,把上面模块串起来。
4.1 客户端发起
beaver-desktop
→ POST /v1/chat/send(经 gateway)
→ chat_api handler
→ chat_rpc.SendMsg
客户端可先做 乐观 UI(本地插一条 sending),等服务端返回 seq 再改 sent——Flutter / PC 都是这个套路。
4.2 服务端处理(SendMsg 核心步骤)
sendmsglogic.go 里逻辑很重,概括成 7 步:
- 鉴权:发送者是否登录、是否有权在该会话发言
- 构建消息体:Proto → 内部
ctype.Msg(支持回复引用、多种 msgType) - 分配 seq:会话内单调递增,是同步锚点
- 写 MySQL:消息表、会话 lastMsg、user_conversation version 等
- 更新 datasync 摘要:供离线端下次增量
- 组装 tableUpdates:把 messages / conversations / user_conversations 变更打成 WS 包体
- 按接收者循环发 MQ:每个群成员一条 MQ(携带 该成员自己的 user_conversation 更新)
第 7 步很关键:群聊 100 人,不是广播一个巨型包,而是每人一条 payload,只带自己的未读/version。这样客户端收到就能直接 upsert 本地表,不必再算「这条消息算不算我的未读」。
4.2 链路示意图
┌──────────┐ REST ┌──────────┐ gRPC ┌──────────┐
│ desktop │ ────────► │ chat_api │ ──────► │ chat_rpc │
└──────────┘ └────┬─────┘ └────┬─────┘
│ │
│ ├── MySQL 写库
│ ├── 更新 datasync
│ └── RocketMQ (Topic: ws)
│ │
│ ▼
│ ┌──────────────┐
│ │ ws_api 消费 │
│ │ SendMsgToUser │
│ └──────┬───────┘
│ │ WebSocket
▼ ▼
返回 seq 给 A 在线成员 B、C、D 收到推送
4.3 离线成员怎么办
用户 B 离线时,ws 推不到。B 下次打开 App:
- 调 datasync 接口:
since=上次时间戳→ 拿到各会话 serverMaxSeq - 和本地
chat_sync_status.seq比,筛出落后会话 - 调 chatSyncApi(fromSeq, toSeq) 批量拉消息
- 落本地 SQLite / Drift,刷新 UI
在线走 WS,离线走 sync,两条路汇到同一张本地消息表——47 号文从客户端角度讲这条线。
5. 三个 IM 后端「必答题」在海狸里怎么答
5.1 消息顺序与 seq
问题:群聊里谁先谁后?编辑、撤回怎么算?
海狸做法:会话内 seq 严格递增,一条消息一个 seq。撤回/编辑是 同 messageId 更新 或新事件类型,客户端按 seq 排序渲染。不要用时间戳 alone 排序(时钟 skew 会乱)。
对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| seq | 顺序可靠、增量 sync 简单 | 需要服务端分配 |
| 时间戳 | 实现直觉 | 多端时钟不一致会乱序 |
| 向量时钟 | 分布式强 | IM 产品很少对用户暴露 |
5.2 写走 REST,推走 WS
问题:为什么发消息不用 WebSocket 上行?
海狸做法:
- REST:鉴权成熟、易日志、易限流、易重试
- WS:下行广播、低延迟
上行也可以走 WS(有些 IM 这样),海狸 2.0 选 REST 写 + MQ + WS 推,对 go-zero 和网关生态更友好。二次开发别在 Vue 组件里直接 ws.send 发消息,除非你要改整条链路。
5.3 多 WS 节点怎么推
问题:部署两个 ws 实例,用户连在 B 节点,chat 怎么知道?
海狸做法:RocketMQ 广播消费,每个 ws 节点都收到 MQ,各自查本机有没有 targetId 的连接,有就推。连接表可在 Redis 维护 userId → nodeId(具体以实现为准)。
这是 IM 后端面试常考题,海狸源码里可以直接对着 mqconsumerlogic.go 讲。
6. 2.0 服务端「底座级」能力拆解
6.1 消息媒体同步
语音「未听 / 已听」不是改消息正文,而是 消息媒体态。2.0 单独做了 mark / sync 接口,datasync 也能拉媒体变更。2.0.1 修过跨库查询导致 PC 听了手机还显示未听——说明 消息体 和 媒体态 分表/分模块是对的。
6.2 用户设置跨端
PC 快捷键等配置走 user 模块 Settings API,存服务端 JSON。换电脑登录拉设置即可——Electron 本地 localStorage 做不了这个。
6.3 OAuth2 链路
开发者门户 beaver-open 建应用 → 用户 beaver-oauth 授权 → auth/open 签发 Token。和 chat 零耦合。接 OA 登录只动 open/auth,别动 sendmsg。
6.4 群推送 bot_public
外部 HTTP POST → 校验 Token → 写群消息 → 同样走 MQ → WS。和 OAuth 配置入口不同、代码路径不同。
6.5 运营 RPC
backend 调 user/chat 做 360 视图、审计、封禁。权限模型和用户 API 分开,避免「一个 JWT 既能聊天又能删库」。
7. 消息类型扩展:二次开发手册
要在海狸里加一种新消息(例如「投票卡片」),建议顺序:
chat_rpc.proto增加PollMsg定义go generate/ go-zero 生成代码sendmsglogic.go的BuildMsgFromProto增加 case- file 服务如需封面仍走 fileUrl
- chat_api 暴露发送接口(若需新 REST 形状)
- beaver-desktop + beaver-flutter 各加渲染 widget / 组件
- datasync / WS 的 tableUpdates 结构若不变,通常不用改 ws 层
漏做第 6 步,就会出现「服务端能存,客户端显示空白」——IM 功能永远是 服务端 + 全客户端一起发。
当前 proto 已支持的主要类型包括:Text、Image、Video、File、Voice、AudioFile、Emoji、Notification、Link、CloudDoc 等,2.0 双端已对接常用 subset。
8. WebSocket 事件:不止 chat_message
| 事件方向 | 示例 | 用途 |
|---|---|---|
| 下行 | chat_message | 新消息、会话列表变更 |
| 下行 | read_seq 更新 | 已读同步 |
| 下行 | typing | 正在输入(服务端有,UI 完善中) |
| 下行 | bot_streaming_* | 机器人 / AI 流式同一条消息更新 |
| 下行 | 好友/群/通知 | 非聊天模块变更 |
流式回复(2.1 智能机器人会产品化)的核心:一个 messageId,多次 chunk,done 时落库全文。不是每个 token insert 一行 MySQL——那是踩坑写法。
9. 存储层:MySQL + Redis + Mongo + MinIO 各干什么
| 存储 | 典型用途 |
|---|---|
| MySQL | 用户、好友、群、会话关系、消息索引 |
| Redis | 缓存、在线态、分布式锁(authlock 等) |
| MongoDB | 部分消息或大文档(按模块) |
| MinIO | 图片、语音、视频、文件 blob |
别问「能不能只用 MySQL」——小 demo 可以,生产 IM 几乎都会 热数据 Redis + 冷数据 MySQL + 对象存储。海狸是按生产级分的,学习成本略高,但扩展路径清晰。
10. 和同类开源 IM 比后端(客观)
| 维度 | 海狸 2.0 | 常见感受 |
|---|---|---|
| 框架 | go-zero | 国内文档多,代码生成省事 |
| 拆分 | chat/ws/open/backend 清晰 | 比单体重构友好 |
| 客户端 | Electron + Flutter 官方双端 | 比「只给 SDK」的引擎型产品上手快 |
| 开放 | OAuth2 + 群 Webhook 已有 | 交互式 @Robot 在 2.1 |
| 运营 | 独立 manager + backend | 私有化团队常用 |
不是「最强」,而是 「能读源码、能改模块、能私有化」 的路线,适合中小团队自建。
11. 源码阅读路线(建议打印贴显示器旁)
第一轮:消息主线(4 小时)
app/chat/chat_api/chat_api.api— 看有哪些 RESTapp/chat/chat_rpc/internal/logic/sendmsglogic.go— 发送全逻辑app/ws/ws_api/internal/logic/mqconsumerlogic.go— MQ → WSapp/datasync/datasync_api— 摘要接口
第二轮:权限与社交(2 小时)
app/friend/app/groupRPCapp/chat/.../recallmessagelogic.go等
第三轮:开放(2 小时)
app/open/open_rpc/open_rpc.protoapp/open/open_portalOAuth 登录逻辑
配合 beaver-docs 在线文档,比盲读快一倍。
12. 常见问题(后端向)
Q:发消息延迟高,查哪里?
先看 chat_rpc 写库耗时,再看 MQ 堆积,最后看 ws 推送。别一上来优化 MySQL 索引,先打链路日志。
Q:群消息部分人收不到?
查该用户是否还在群、是否被禁言、WS 是否在线、离线是否 sync 了 seq。在线问题查 MQ 和 ws 连接;离线问题查 datasync + chatSync。
Q:能不能去掉 RocketMQ?
架构上 chat 直接调 ws 可以,但多实例 ws 会很难做。生产不建议。
Q:OAuth 和聊天要同一个服务吗?
不要。海狸已经拆开,保持边界。
13. 后续系列预告:一篇一个技术点
IM 复杂,完全可以像您说的 「拆技术点 + 分析」 连续写很多篇,例如:
| 计划篇目 | 内容 |
|---|---|
| 消息 seq 与 datasync 协议详解 | 摘要结构、冲突处理 |
| WebSocket 连接管理与心跳 | ws 模块源码 walkthrough |
| OAuth2 在海狸里的 Code 流转 | 配合 45 号文深挖 |
| 群推送 Webhook 与 bot_public | curl 示例 + 验签 |
| LiveKit 音视频接入 | call 模块 |
| 消息媒体态与跨端同步 | markMessageMedia 链路 |
| beaver-manager 运营 RPC 设计 | backend 权限 |
本篇是 总览 + 消息主线;下一篇 47 讲 双端客户端怎么消费这套协议。
14. 小结
海狸 IM 2.0 服务端的价值,对开发者来说在于:
- go-zero 多模块,聊天 / 同步 / 推送 / 开放 / 运营边界清楚
- SendMsg → MQ → WS 链路完整,适合学习生产级 IM
- seq + datasync 支撑双端离线增量
- fileUrl 统一媒体,proto 扩展路径清晰
- 源码全开源,能 fork、能改、能写系列文章
如果你正在选型开源 IM、或准备面试 IM 后端,建议 clone beaver-server,对着 sendmsglogic.go 和 mqconsumerlogic.go 走一遍调试——比看十篇概论有用。
相关链接
项目源码:
- 后端源码:https://github.com/wsrh8888/beaver-server
- 移动端Flutter源码:https://github.com/wsrh8888/beaver-flutter
- PC端源码:https://github.com/wsrh8888/beaver-desktop.git
- 后台管理系统源码:https://github.com/wsrh8888/beaver-manager
- 开放平台门户源码:https://github.com/wsrh8888/beaver-open
- OAuth 授权登录页源码:https://github.com/wsrh8888/beaver-oauth
学习资源:
- 在线文档:https://wsrh8888.github.io/beaver-docs/
核心教学视频:
- 本地搭建教程合集:https://space.bilibili.com/269553626/lists/6075764?type=season
- 服务器部署教程合集:https://space.bilibili.com/269553626/lists/6075828?type=season
上一篇:[海狸IM 2.0 开放能力说明:OAuth2 接入与群推送机器人](45.海狸IM 2.0:开放平台、OAuth与Bot,让IM不止于聊天.md)
下一篇:[海狸IM 2.0 双端客户端技术实践:Electron 与 Flutter 如何对齐 IM 协议](47.海狸IM 2.0 双端客户端技术实践:Electron与Flutter如何对齐IM协议.md)
更多推荐

所有评论(0)