44 KiB
IM Kafka 接入方案
日期:2026-08-26
关联文档:IM Kafka 接入范围识别、IM 前后端逐图对接协议
目标:明确 Admin 后台、IM 服务、HotGo 控制库、租户业务库之间的 Kafka 接入边界、查询方式、鉴权方式和首批落地范围。
0. 与范围识别文档的关系
IM Kafka 接入范围识别 是本方案的前置输入,负责按前后端对接文档逐章识别哪些业务适合 Kafka、哪些仍应走 WebSocket/Redis/MySQL/OSS。本方案在该识别结果基础上,进一步确定服务边界、库访问边界、前后台交互模型、内部查询接口、鉴权方式和首批落地步骤。
两份文档分工如下:
| 文档 | 作用 |
|---|---|
| IM Kafka 接入范围识别 | 回答“哪些业务该用 Kafka,哪些不该用”。 |
| 本方案 | 回答“Kafka 怎样接入,Admin 与 IM 怎样交互,服务和库怎样隔离”。 |
实施时以范围识别文档的逐章清单确定业务覆盖面,以本方案确定技术实现方式。若后续新增需求,先补充范围识别,再同步调整本方案的 Topic、Command、内部接口和鉴权规则。
1. 核心原则
系统按“服务和库一一对应”设计,不允许不同服务直连对方数据库。
| 服务 | 只能直接访问 | 说明 |
|---|---|---|
| Admin/HotGo 服务 | hotgo 控制库 |
管理后台、租户主数据、后台配置、审核、操作单。 |
| IM 服务 | 当前租户业务库 | 用户、好友、群、消息、文件引用、工作台业务数据。 |
| Redis | IM 实时状态与缓存 | 在线状态、短状态、热点消息、缓存分层。 |
| Kafka | Admin 与 IM 的命令、事件、结果桥 | 后台写操作、异步任务、业务事件、审计、缓存失效。 |
| OSS | 文件对象存储 | 上传下载字节流不进入 Kafka。 |
最终边界:
- Admin 不直连租户库。
- IM 不直连
hotgo库。 - Admin 写租户业务操作统一通过 Kafka Command。
- IM 写后台业务状态不直写
hotgo,通过 Kafka Result/Event 返回 Admin。 - Admin 查询租户业务数据通过 IM 内部只读 API。
- IM 查询租户配置、字典、路由通过 Admin 内部只读 API,或消费 Kafka 配置事件维护本地副本。
- 在线实时投递继续走 Redis + WebSocket,Kafka 不替代 WS 主链路。
2. 总体架构
Admin/HotGo 服务
-> 写 hotgo 操作单
-> 生产 Kafka Command
-> IM 服务消费 Command
-> 写租户业务库
-> 生产 Kafka Result/Event
-> Admin 消费 Result/Event
-> 更新 hotgo 操作状态
Admin 查询租户业务数据
-> 调 IM 内部只读 API
-> IM 查租户业务库
-> 返回 Admin
IM 查询租户配置/字典/路由
-> 优先读本地缓存/本地副本
-> 缓存缺失时调 Admin 内部只读配置 API
-> Admin 查 hotgo
-> 返回 IM
3. Kafka 使用范围
| 场景 | 是否使用 Kafka | 说明 |
|---|---|---|
| Admin 后台群操作通知 C 端 | 是 | 后台写 hotgo 操作单,投 Kafka,IM 执行租户库变更并 WS 下发。 |
| Admin 后台用户封禁、解封、重置密码 | 是 | IM 消费后更新租户库、踢在线、通知客户端。 |
| Admin 后台租户配置、字典、功能开关变更 | 是 | Admin 写 hotgo 后投配置事件,IM 刷新缓存或本地副本。 |
| 企业注册、实名、举报、反馈审核结果 | 是 | Admin 审核后投 Kafka,IM 生成通知或更新租户侧状态。 |
| 工作台会议、日报、月报、信箱通知 | 是 | 状态变更、提醒、红点更新走 Kafka。 |
| 私聊/群聊消息异步落库 | 是 | WS 收消息后完成鉴权、幂等和 seq,再投 Kafka 异步持久化。 |
| 文件安全检测、恶意文件检测 | 是 | OSS 上传完成后投检测任务,消费者调用第三方并更新状态。 |
| 离线 Push | 是 | 消息或业务事件落地后投 Push 任务。 |
| 在线消息实时 fanout | 不作为主链路 | 继续 Redis Pub/Sub/Stream + WS。 |
| 文件上传下载字节流 | 否 | 走 OSS/文件服务;Kafka 只处理上传完成后的事件。 |
| 验证码、Action Token、限流、在线状态 | 否 | 继续 Redis。 |
| 普通查询接口 | 否 | 走内部只读 API 或本服务数据库。 |
4. 前后台交互模型
4.1 后台写租户业务
Admin 后台所有会影响租户业务库或 C 端展示的写操作,统一走 Kafka Command。
Admin
1. 校验后台管理员权限
2. 写 hotgo.admin_command,状态 pending
3. 发送 Kafka command
IM
4. 消费 command
5. 校验租户、对象、版本、权限和幂等
6. 写租户业务库
7. 必要时通过 WS 通知在线 C 端
8. 发送 Kafka result
Admin
9. 消费 result
10. 更新 hotgo.admin_command 状态
典型操作:
| 操作 | Command | IM 执行动作 | C 端通知 |
|---|---|---|---|
| 群资料修改 | group.profile.update |
更新群表和 Topic 资料 | group.profile.updated |
| 设置/删除管理员 | group.admin.set / group.admin.remove |
更新成员角色 | group.role.updated |
| 禁言/解禁 | group.member.mute / group.member.unmute |
更新禁言表 | group.mute.updated |
| 踢人 | group.member.remove |
更新成员状态和订阅 | group.member.removed |
| 解散群 | group.delete |
解散 Topic、撤销权限 | group.deleted |
| 用户封禁 | user.disable |
更新用户状态、踢在线 | user.disabled |
| 重置密码 | user.password.reset |
更新密码和 auth_version |
可选安全通知 |
4.2 后台查询租户业务
Kafka 不适合做普通查询。Admin 需要实时查看租户业务数据时,调用 IM 提供的内部只读 API。
只读 API 只允许 GET,不提供通用 SQL 或万能查询接口。
| 接口 | 用途 |
|---|---|
GET /internal/admin-query/tenants/{tenant_code}/users/{user_id} |
查询租户用户详情。 |
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic} |
查询群详情。 |
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/members |
查询群成员。 |
GET /internal/admin-query/tenants/{tenant_code}/messages/{topic}/{seq} |
查询消息受控快照。 |
GET /internal/admin-query/tenants/{tenant_code}/files/{file_id} |
查询文件引用和审核状态。 |
GET /internal/admin-query/tenants/{tenant_code}/reports/abuse/{report_id}/target |
查询举报目标快照。 |
GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters |
查询 IM 租户库 Kafka 死信列表。 |
GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters/{dead_id} |
查询单条死信详情和原始 envelope。 |
后台列表、统计、高频页面优先使用 hotgo 摘要表。摘要由 IM 通过 Kafka 事件同步给 Admin,Admin 查询自己的库。
推荐摘要表:
| 摘要表 | 来源 |
|---|---|
admin_im_user_snapshot |
IM 用户资料变更事件。 |
admin_im_group_snapshot |
IM 群资料、成员数、状态变更事件。 |
admin_im_group_member_stat |
群成员统计事件。 |
admin_im_file_audit_snapshot |
文件审核和引用状态事件。 |
admin_im_workbench_snapshot |
工作台红点、会议、日报/月报状态事件。 |
4.3 IM 查询后台配置
IM 不直连 hotgo。IM 需要租户路由、租户状态、字典、第三方配置时,使用两种方式:
- 常态:Admin 修改配置后发送 Kafka 配置事件,IM 消费后更新进程内缓存或本地副本。
- 兜底:IM 缓存缺失或启动预热时,调用 Admin 内部只读配置 API。
| 接口 | 用途 |
|---|---|
GET /internal/im-config/tenants/{tenant_code} |
查询租户状态、名称、路由标识。 |
GET /internal/im-config/tenants/{tenant_code}/limits |
查询人数、群数、上传大小等限制。 |
GET /internal/im-config/tenants/{tenant_code}/features |
查询功能开关。 |
GET /internal/im-config/dictionaries?types=oss,file_security,sms,jpush,agora |
查询字典配置。 |
GET /internal/im-config/app/releases/latest |
查询 App 版本配置。 |
Admin 内部配置 API 只返回 IM 运行需要的字段,不返回后台管理员账号、菜单权限、审核私有字段。
5. Topic 规划
| Topic | 用途 |
|---|---|
im.admin.command |
Admin 发给 IM 的租户业务写命令。 |
im.admin.command.result |
IM 回传命令执行结果。 |
im.config.event |
Admin 发给 IM 的配置、字典、租户状态变更。 |
im.business.event |
IM 内部或 Admin 触发的 C 端业务事件。 |
im.message.persist |
私聊、群聊消息异步落库。 |
im.message.push |
离线 Push 任务。 |
im.file.security |
文件内容安全、恶意文件检测任务。 |
im.file.lifecycle |
文件引用绑定、释放、回收、合规删除。 |
im.workbench.event |
会议、日报、月报、信箱、工作台红点事件。 |
im.cache.invalidate |
跨节点缓存失效。 |
im.audit.event |
审计事件。 |
im.dead.letter |
消费失败死信。 |
首版可合并部分 Topic,但事件体必须保留 biz_type 和 event_type,方便后续拆分。
6. 消息结构
统一 Kafka Envelope:
{
"event_id": "evt_01K...",
"trace_id": "req_01K...",
"tenant_code": "default",
"source_service": "hotgo-admin",
"target_service": "im",
"event_type": "group.member.mute",
"biz_type": "group",
"biz_id": "grp...",
"idempotency_key": "idem_01K...",
"version": 3,
"occurred_at": "2026-08-26T10:00:00.000Z",
"actor": {
"type": "admin",
"id": "admin_1",
"display_name": "管理员"
},
"payload": {}
}
Command Result:
{
"event_id": "evt_result_01K...",
"trace_id": "req_01K...",
"tenant_code": "default",
"command_id": "cmd_01K...",
"command_event_id": "evt_01K...",
"status": "succeeded",
"reason": "",
"message": "",
"result": {},
"occurred_at": "2026-08-26T10:00:01.000Z"
}
失败时 status=failed,reason 使用稳定错误码,例如 GROUP_VERSION_CONFLICT、FORBIDDEN、RESOURCE_NOT_FOUND、COMMAND_DUPLICATED。
7. 幂等、事务与重试
7.1 生产端
Admin 写操作必须先落 hotgo.admin_command,再发送 Kafka。推荐使用 outbox 模式:
- 同一事务写
admin_command和admin_outbox。 - outbox poller 发送 Kafka。
- Kafka 发送成功后标记 outbox 已发送。
- 发送失败按退避重试。
IM 内部业务事件也按同样方式写租户库 outbox,避免“数据库成功、事件丢失”。
7.2 消费端
每个消费者维护消费幂等记录:
| 字段 | 说明 |
|---|---|
event_id |
Kafka 事件唯一 ID。 |
consumer_group |
消费组。 |
status |
processing/succeeded/failed/dead。 |
retry_count |
重试次数。 |
last_error |
最近失败原因。 |
created_at/updated_at |
时间。 |
消费前先插入或锁定 event_id + consumer_group。已成功的事件直接跳过;处理中超时可重新抢占。业务表必须再用唯一键兜底,例如 tenant + command_id、tenant + client_mid、tenant + topic + seq。
7.3 重试与死信
| 类型 | 策略 |
|---|---|
| 临时失败 | 指数退避重试。 |
| 版本冲突 | 不自动重试,回传 failed,由 Admin 刷新后重新发命令。 |
| 权限失败 | 不重试。 |
| 对象不存在 | 不重试,除非明确是延迟同步。 |
| 第三方失败 | 可重试,超过次数写死信和失败表。 |
7.4 消费失败处理流程
消费者收到消息后按固定流程处理:
收到 Kafka 消息
-> 校验 Envelope 格式、签名字段、租户和事件类型
-> 抢占 event_id + consumer_group 幂等记录
-> 已 succeeded:提交 offset,跳过
-> processing 超时:允许重新抢占
-> 执行业务逻辑
-> 成功:标记 succeeded,提交 offset
-> 可重试失败:retry_count + 1,按退避策略延迟重试
-> 超过最大重试:写入死信,标记 dead,提交 offset
-> 不可重试失败:标记 failed 或 dead,必要时回传 command result,提交 offset
重试建议参数:
| 参数 | 建议值 | 说明 |
|---|---|---|
max_retry_count |
5~10 | 第三方接口可取高值,权限/参数错误不重试。 |
retry_backoff |
10s、30s、1m、5m、15m | 指数退避,可加随机抖动。 |
processing_timeout |
5m~30m | 超过后允许其他消费者重新抢占。 |
dead_letter_retention |
30~90 天 | 便于排障和人工补偿。 |
7.5 死信队列处理
进入死信表示该事件已超过自动恢复能力,系统不能再无限重试。死信必须形成“可查看、可诊断、可重放、可关闭”的闭环。
死信写入两处:
- Kafka
im.dead.letterTopic:保留原始事件,便于流式订阅、告警和后续自动补偿。 - 数据库失败表:便于 Admin 后台查询、人工处理和审计。
推荐失败表字段:
| 字段 | 说明 |
|---|---|
dead_id |
死信记录 ID。 |
event_id |
原 Kafka 事件 ID。 |
source_topic |
原 Topic。 |
consumer_group |
失败消费组。 |
tenant_code |
租户编码。 |
event_type |
事件类型。 |
biz_type / biz_id |
业务对象。 |
payload |
原始 Envelope。 |
retry_count |
已重试次数。 |
last_reason |
稳定失败原因。 |
last_error |
最近错误摘要,敏感字段脱敏。 |
status |
dead/pending_replay/replayed/ignored/resolved。 |
replay_count |
人工重放次数。 |
created_at/updated_at |
时间。 |
resolved_by/resolved_at |
人工处理人和处理时间。 |
resolve_note |
处理备注。 |
死信来源分类:
| 分类 | 示例 | 默认处理 |
|---|---|---|
| 临时资源失败超过上限 | DB 短暂不可用、第三方超时 | 修复环境后允许重放。 |
| 业务状态冲突 | 群版本冲突、对象已被删除 | 不自动重放,Admin 刷新状态后重新发命令。 |
| 权限失败 | 管理员无权限、租户停用 | 不重放,标记 ignored 或 failed result。 |
| 参数错误 | Envelope 缺字段、payload 非法 | 不重放,修复生产方后关闭。 |
| 第三方永久失败 | 文件违规、Push token 无效 | 按业务结果落状态,不重放或仅重放特定步骤。 |
Admin 后台需要提供死信处理页:
| 操作 | 说明 |
|---|---|
| 查看详情 | 展示事件、租户、业务对象、错误原因、重试次数和 trace_id。 |
| 重放 | 将原事件或修正后的事件重新投递到原 Topic,生成新的 event_id,保留 parent_event_id。 |
| 忽略 | 确认该事件无需继续处理,状态改为 ignored。 |
| 标记已处理 | 线下补偿完成后改为 resolved。 |
| 重新生成命令 | 对版本冲突类命令,不直接重放旧事件,而是基于最新状态创建新 Command。 |
重放规则:
- 重放必须生成新的
event_id,原event_id保留在parent_event_id。 - 重放前重新校验租户状态、对象是否存在、操作者权限和业务版本。
- 参数错误、权限失败、版本冲突默认禁止直接重放。
- 文件安全检测、Push、审计写入等可重试任务允许重放。
- 每次重放都写审计,记录处理人、原因、原事件和新事件。
死信告警:
| 指标 | 建议 |
|---|---|
| 单 Topic 死信数 | 5 分钟内超过阈值告警。 |
同一 event_type 连续失败 |
可能是代码或配置问题,立即告警。 |
| 同一租户大量失败 | 检查租户库、配置或权限状态。 |
| 死信积压时间 | 超过 24 小时未处理提醒。 |
对 Admin Command 类事件,进入死信后必须回传 im.admin.command.result,状态为 failed 或 dead,让后台操作单结束等待状态。否则后台会长期显示处理中。
8. 内部接口鉴权
内部 API 鉴权非常关键,必须同时做网络层、服务层和业务层控制。
8.1 网络层
/internal/*只允许内网网关或内网地址访问。- 禁止公网直接暴露内部接口。
- 有条件时启用 mTLS。
8.2 服务签名
内部请求统一携带:
| Header | 说明 |
|---|---|
X-Service-Name |
调用方服务名,例如 hotgo-admin 或 im-server。 |
X-Timestamp |
Unix 秒或毫秒时间戳。 |
X-Nonce |
随机串,短期内不可重复。 |
X-Request-ID |
请求追踪 ID。 |
X-Signature |
HMAC 签名。 |
签名内容:
METHOD + "\n" +
PATH_WITH_QUERY + "\n" +
BODY_SHA256 + "\n" +
X-Timestamp + "\n" +
X-Nonce
校验规则:
- 时间窗建议 5 分钟。
nonce写 Redis,TTL 5 分钟,防重放。- 每个服务独立
service_id/service_secret。 - 每个服务配置允许访问的内部接口白名单。
8.3 业务授权
Admin 调 IM 查询时,签名 payload 或 Header 中必须包含后台操作者信息:
| 字段 | 说明 |
|---|---|
admin_user_id |
后台用户 ID。 |
admin_role |
后台角色。 |
admin_permissions |
本次已校验过的权限列表。 |
tenant_code |
被查询租户。 |
reason |
查询原因或工单号。 |
IM 仍需根据 tenant_code + permission + resource 做二次校验,并写内部查询审计。
IM 调 Admin 配置接口时,Admin 只校验服务身份和接口白名单,不返回超出 IM 运行需要的字段。
9. 文件与工作台
9.1 文件
文件上传下载不走 Kafka,OSS 是文件字节流主链路。Kafka 只处理上传完成后的事件:
im.file.security:内容安全、恶意文件检测。im.file.lifecycle:业务引用绑定、释放、临时文件回收、合规删除。im.business.event:文件审核失败、合规处置等 C 端通知。
9.2 工作台
工作台查询不走 Kafka,状态变化和提醒走 Kafka:
- 会议创建、更新、取消、提醒、响应:
im.workbench.event。 - 日报/月报提交、撤回、重开、催办:
im.workbench.event。 - 总经理信箱提交、处理结果:
im.workbench.event+im.audit.event。 - 工作台模块配置变更:
im.config.event或im.cache.invalidate。
10. 首批落地建议
| 阶段 | 内容 |
|---|---|
| 第一步 | 增加 Kafka 配置、生产者、消费者、健康检查、统一 Envelope。 |
| 第二步 | 增加消费幂等表、outbox 表、死信表或死信 Topic。 |
| 第三步 | 接入 im.admin.command 和 im.admin.command.result,先覆盖后台群治理操作。 |
| 第四步 | 增加 IM 内部只读查询 API,供 Admin 查用户、群、消息、文件快照。 |
| 第五步 | 增加 Admin 内部配置 API,供 IM 查询租户配置、字典、第三方参数。 |
| 第六步 | 接入文件安全检测、离线 Push、工作台事件。 |
| 第七步 | 消息异步落库接入 im.message.persist,完善幂等和重试。 |
11. 分端任务拆分
11.1 Admin/HotGo 侧任务
Admin 侧只访问 hotgo 库,负责后台操作入口、配置主数据、命令生产、结果消费和死信处理。
| 模块 | 任务 | 产出 |
|---|---|---|
| Kafka 基础 | 增加 Kafka producer/consumer 配置、健康检查、统一 Envelope 组装。 | 可发送 im.admin.command、消费 im.admin.command.result。 |
| 操作单 | 新增 admin_command、admin_outbox 表和状态机。 |
pending/running/succeeded/failed/dead 操作状态。 |
| Outbox | 后台写操作单与 outbox 同事务落库,poller 发送 Kafka。 | 防止 hotgo 写成功但 Kafka 丢事件。 |
| Command 生产 | 群治理、用户封禁/解封、重置密码、审核结果、工作台配置等后台操作生成 Command。 | im.admin.command 事件。 |
| Result 消费 | 消费 IM 回传结果并更新操作单。 | 后台页面可展示最终成功/失败原因。 |
| 配置事件 | 租户配置、字典、功能开关、第三方配置变更后发送 im.config.event。 |
IM 刷新缓存或本地副本。 |
| 配置查询 API | 提供 /internal/im-config/* 只读接口。 |
IM 启动预热和缓存缺失兜底查询。 |
| 摘要表 | 建立用户、群、文件、工作台等后台摘要表。 | 后台列表统计优先查 hotgo。 |
| 死信后台 | 提供死信查看、重放、忽略、标记已处理、重新生成命令。 | 运维闭环。 |
| 内部鉴权 | 实现服务签名校验、nonce 防重放、接口白名单、审计。 | 保护 Admin 内部配置 API。 |
| 审计 | 记录后台操作者、租户、资源、Command、Result、死信处理。 | 可追踪后台操作链路。 |
Admin 首批优先级:
- Kafka 基础配置、Envelope、producer。
admin_command+admin_outbox。- 群治理 Command 生产。
im.admin.command.result消费。/internal/im-config/*配置查询 API。- 死信管理页。
11.2 IM Server 侧任务
IM Server 侧只访问租户业务库,负责执行 Admin Command、维护租户业务状态、通过 WS 通知 C 端,并向 Admin 回传结果。
| 模块 | 任务 | 产出 |
|---|---|---|
| Kafka 基础 | 增加 Kafka producer/consumer 配置、健康检查、统一 Envelope 解析。 | 可消费 Command、生产 Result/Event。 |
| 消费幂等 | 新增租户库消费幂等表或统一消费日志。 | event_id + consumer_group 幂等。 |
| Command 消费 | 消费 im.admin.command。 |
后台命令进入 IM 执行链路。 |
| 群治理执行器 | 执行群资料、管理员、禁言、踢人、解散、群主转让等命令。 | 更新租户库和 Topic 状态。 |
| 用户治理执行器 | 执行封禁、解封、重置密码、踢在线等命令。 | 更新用户状态和 auth_version。 |
| Result 生产 | 执行成功/失败后发送 im.admin.command.result。 |
Admin 操作单闭环。 |
| WS 通知 | Command 成功后生产 C 端业务事件并投递在线客户端。 | `info.what="im |
| 配置消费 | 消费 im.config.event,刷新进程内缓存或本地副本。 |
租户配置、字典、第三方配置生效。 |
| 配置兜底查询 | 调 Admin /internal/im-config/*。 |
IM 启动预热和缓存缺失恢复。 |
| 内部只读 API | 提供 /internal/admin-query/*。 |
Admin 实时查询租户业务数据。 |
| 文件任务 | 上传完成后生产/消费 im.file.security、im.file.lifecycle。 |
文件审核、引用绑定、回收。 |
| 消息异步落库 | 接入 im.message.persist。 |
私聊/群聊消息削峰落库。 |
| Push 任务 | 生产 im.message.push。 |
离线 Push 异步投递。 |
| 死信处理 | 写失败表、投 im.dead.letter、Command 死信回传 Result。 |
失败可诊断、可补偿。 |
| 内部鉴权 | 校验 Admin 调用 /internal/admin-query/* 的签名、权限和审计。 |
保护租户业务查询。 |
IM Server 首批优先级:
- Kafka 基础配置、Envelope、consumer/producer。
- 消费幂等表和死信写入。
im.admin.command群治理消费执行。im.admin.command.result回传。- WS 业务事件下发。
/internal/admin-query/*只读查询 API。
11.3 双方联调契约
| 契约 | Admin 负责 | IM Server 负责 |
|---|---|---|
| Envelope 字段 | 生产完整字段,保证 event_id/trace_id/tenant_code/event_type/idempotency_key。 |
校验字段完整性和事件类型白名单。 |
| Command 状态 | 创建操作单,展示状态。 | 回传成功、失败、死信结果。 |
| 幂等 | 同一后台操作复用 command_id/idempotency_key。 |
同一 Command 重复消费只执行一次。 |
| 错误码 | 展示 IM 返回的稳定 reason。 |
返回稳定 reason,不让 Admin 解析错误文本。 |
| 查询接口 | 带管理员身份、权限、原因和签名。 | 校验签名、权限和租户资源,写审计。 |
| 配置接口 | 提供最小必要配置字段。 | 不直连 hotgo,使用缓存、事件或内部 API。 |
| 死信 | 提供处理入口和人工操作审计。 | 写死信、回传 Command dead/failed 状态。 |
| Trace | 生成并传递 trace_id。 |
日志、Kafka、WS 下发沿用同一 trace_id。 |
11.4 首批联调用例
| 用例 | 预期 |
|---|---|
| Admin 禁言群成员 | Admin 操作单 pending,IM 消费后更新租户库,C 端收到 group.mute.updated,Admin 状态 succeeded。 |
| Admin 重复提交同一禁言操作 | IM 只执行一次,Result 返回同一最终状态。 |
| Admin 删除管理员但群版本冲突 | IM 返回 GROUP_VERSION_CONFLICT,Admin 操作单 failed,不自动重试。 |
| IM 消费时临时 DB 失败 | 自动退避重试,未超限不进入死信。 |
| IM 消费超过重试上限 | 写 im.dead.letter,Admin 操作单进入 dead/failed。 |
| Admin 查询群成员 | Admin 调 /internal/admin-query/*,IM 校验签名和权限后返回只读数据。 |
| IM 启动加载租户配置 | IM 优先读缓存,缺失时调 /internal/im-config/*,Admin 只返回必要配置。 |
11.5 IM Server 已接入的首批 Admin Command
IM Server 首批消费 im.admin.command,执行后回传 im.admin.command.result。所有命令必须带 tenant_code,payload.actor_user_id 必须是租户内 IM 用户 ID,用于落群事件和 C 端通知。
Command event_type |
说明 | 必填 payload |
|---|---|---|
group.profile.update |
修改群名称、头像、简介。 | command_id,actor_user_id,topic,profile,version |
group.governance.update |
修改进群审批、全员禁言、成员邀请等治理开关。 | command_id,actor_user_id,topic,governance,version |
group.admin.set |
设置一个或多个管理员。 | command_id,actor_user_id,topic,user_ids |
group.admin.remove |
删除管理员,降为普通成员。 | command_id,actor_user_id,topic,user_id |
group.member.mute |
禁言成员。 | command_id,actor_user_id,topic,user_id,reason,expires_at |
group.member.unmute |
解除禁言。 | command_id,actor_user_id,topic,user_id |
group.member.remove |
移出群成员。 | command_id,actor_user_id,topic,user_id |
group.owner.transfer |
转让群主。 | command_id,actor_user_id,topic,target_user_id,version |
当前 IM Server 消费 im.admin.command 时已经接入 im_kafka_consume_log:
- 按
event_id + consumer_group抢占消费。 - 已成功事件直接跳过,不重复执行业务。
- 处理中且未超时的事件跳过,避免并发重复执行。
- 失败或处理超时的事件允许重新抢占。
- 不可重试错误或超过重试次数后写入
im_kafka_dead_letters,并投递im.dead.letter。 - Admin Command 进入死信时回传
im.admin.command.result,status=dead。
11.6 IM Server 已接入的配置事件
IM Server 首批消费 im.config.event,用于 Admin 修改 HotGo 配置后通知 IM 刷新进程内缓存。当前实现以“缓存失效”为主,收到事件后清除对应缓存,下次业务读取时重新加载。
Config event_type |
说明 | IM 行为 |
|---|---|---|
tenant.updated |
租户基础信息变更。 | 清理租户 code/id 缓存。 |
tenant.status.changed |
租户启停、状态变化。 | 清理租户 code/id 缓存。 |
tenant.limits.updated |
租户人数、群数、上传限制等变化。 | 清理租户 code/id 缓存。 |
tenant.features.updated |
租户功能开关变化。 | 清理租户 code/id 缓存。 |
dictionary.updated |
字典配置变化。 | 清理第三方/字典配置缓存。 |
third_party.updated |
OSS、文件安全、短信、极光、声网等第三方配置变化。 | 按 group 清理第三方配置缓存。 |
registration.config.updated |
注册、短信开关变化。 | 清理注册配置缓存。 |
事件示例:
{
"event_id": "evt_01K_config",
"trace_id": "req_01K",
"tenant_code": "default",
"source_service": "hotgo-admin",
"target_service": "im-server",
"event_type": "third_party.updated",
"biz_type": "config",
"biz_id": "im_third_party",
"occurred_at": "2026-08-26T10:05:00.000Z",
"payload": {
"tenant_code": "default",
"group": "im_third_party",
"keys": ["oss.endpoint", "file_security.enabled"]
}
}
禁言命令示例:
{
"event_id": "evt_01K_mute",
"trace_id": "req_01K",
"tenant_code": "default",
"source_service": "hotgo-admin",
"target_service": "im-server",
"event_type": "group.member.mute",
"biz_type": "group",
"biz_id": "grp...",
"idempotency_key": "cmd_01K_mute",
"occurred_at": "2026-08-26T10:00:00.000Z",
"payload": {
"command_id": "cmd_01K_mute",
"actor_user_id": "usr_admin",
"topic": "grp...",
"user_id": "usr_target",
"reason": "刷屏",
"expires_at": null
}
}
11.7 IM Server 已接入的内部只读查询 API
为满足“Admin 不直连租户库”的边界,IM Server 提供内部只读查询接口给 HotGo/Admin 调用。接口默认关闭,通过 internal_api.enabled=true 开启。
请求统一鉴权头:
| Header | 说明 |
|---|---|
X-Service-Name |
调用方服务名,如 hotgo-admin。 |
X-Timestamp |
Unix 秒级时间戳,默认 300 秒时间窗。 |
X-Nonce |
一次性随机串,用于防重放。 |
X-Request-ID |
调用链路 ID。 |
X-Signature |
HMAC-SHA256 签名,支持 hex 或 base64,可带 sha256= 前缀。 |
nonce 防重放优先使用 Redis 短状态 SETNX + TTL,Redis 未配置时退化为单节点进程内防重放;生产多节点必须配置 Redis。
签名明文:
METHOD + "\n" +
PATH_WITH_QUERY + "\n" +
SHA256_HEX(BODY) + "\n" +
X-Timestamp + "\n" +
X-Nonce
当前首批接口:
| 接口 | 说明 |
|---|---|
GET /internal/admin-query/tenants/{tenant_code}/users/{user_id} |
查询租户内用户基础资料。 |
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic} |
查询群资料和治理配置。 |
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/members |
查询群成员分页快照,支持 limit/cursor/role/eligible_role/q。 |
GET /internal/admin-query/tenants/{tenant_code}/messages/{topic}/{seq_id} |
查询单条私聊或群聊消息,seq_id 为 BIGINT。 |
GET /internal/admin-query/tenants/{tenant_code}/files/{file_id} |
查询文件元信息和存储位置。 |
配置示例:
{
"internal_api": {
"enabled": true,
"time_window_seconds": 300,
"services": {
"hotgo-admin": {
"secret": "replace-with-random-secret",
"allow": ["admin_query"]
}
}
}
}
Docker 环境变量:
| 环境变量 | 说明 |
|---|---|
INTERNAL_API_ENABLED |
是否开启内部接口,默认 false。 |
INTERNAL_API_TIME_WINDOW_SECONDS |
签名时间窗。 |
INTERNAL_API_HOTGO_SERVICE_NAME |
Admin 服务名,默认 hotgo-admin。 |
INTERNAL_API_HOTGO_SECRET |
Admin 与 IM 共享签名密钥。 |
11.8 IM 访问 Admin 配置查询接口
为满足“IM 服务不直连 hotgo 库”的目标,IM Server 增加 Admin 内部配置查询客户端。该客户端默认关闭,开启后 store.Tenants、注册短信开关、第三方配置读取会优先请求 Admin 内部接口,失败时仍可回落旧数据库适配器,便于灰度迁移。
配置示例:
{
"admin_internal_api": {
"enabled": true,
"base_url": "http://hotgo-admin:8080",
"service_name": "im-server",
"secret": "replace-with-random-secret",
"timeout_ms": 3000
}
}
接口约定:
| 接口 | 用途 |
|---|---|
GET /internal/im-config/tenants/by-code/{tenant_code} |
按企业码获取租户路由信息,需返回 id/code/name/state/db_name/db_dsn。 |
GET /internal/im-config/tenants/by-id/{tenant_id} |
按租户 ID 获取租户路由信息。 |
GET /internal/im-config/tenants?state=active&limit=1000 |
获取活跃租户列表,用于 Kafka Outbox 扫描。 |
GET /internal/im-config/registration/sms |
获取注册短信开关,返回 {"enabled": true}。 |
GET /internal/im-config/third-party/{group}?keys=a,b |
获取第三方配置,返回 {"values": {"a": "..."}}。 |
鉴权沿用内部接口 HMAC 方案,请求头为 X-Service-Name、X-Timestamp、X-Nonce、X-Signature;签名文本为 METHOD + "\n" + RequestURI + "\n" + BodySHA256 + "\n" + Timestamp + "\n" + Nonce。
Docker 环境变量:
| 环境变量 | 说明 |
|---|---|
ADMIN_INTERNAL_API_ENABLED |
是否启用 IM 到 Admin 的配置查询客户端,默认 false。 |
ADMIN_INTERNAL_API_BASE_URL |
Admin 内部接口根地址。 |
ADMIN_INTERNAL_API_SERVICE_NAME |
IM 服务名,默认 im-server。 |
ADMIN_INTERNAL_API_SECRET |
Admin 与 IM 共享签名密钥。 |
ADMIN_INTERNAL_API_TIMEOUT_MS |
请求超时时间。 |
11.9 IM Server 已接入的消息异步落库
IM Server 已接入 im.message.persist,用于把在线发送链路和最终消息落库解耦。
当前处理方式:
- WS/HTTP 发送入口先同步完成权限校验、业务幂等、
seq_id分配和 Topic 游标占用。 - Kafka 开启时,发送入口先写租户库
im_kafka_outbox,由 Outbox worker 可靠生产im.message.persist,客户端仍立即拿到已分配的seq_id。 - Kafka 关闭时,继续走原同步落库路径,兼容本地和未接 Kafka 环境。
- 消费者按
message_kind区分私聊与群聊:p2p写入im_messages。group写入im_group_messages。
- 消费侧复用
im_kafka_consume_log做event_id + consumer_group幂等。 - 失败按统一退避重试,超过上限写入
im_kafka_dead_letters并投递im.dead.letter。
11.10 IM Server 已接入的缓存失效消费
IM Server 已订阅 im.cache.invalidate,用于接收 Admin 或其他节点发出的通用缓存失效事件。消费侧复用 im_kafka_consume_log 做幂等,失败按统一退避重试,超过上限进入死信。
事件体示例:
{
"event_id": "cache:tenant:default:1720000000",
"event_type": "cache.tenant.invalidate",
"tenant_code": "default",
"payload": {
"tenant_code": "default",
"tenant_id": 1,
"namespace": "tenant"
}
}
支持的失效范围:
event_type / namespace |
处理方式 |
|---|---|
cache.tenant.invalidate / tenant |
清理租户注册表与租户配置进程内缓存。 |
cache.registration.invalidate / registration |
清理注册短信开关缓存。 |
cache.third_party.invalidate / third_party |
按 group 清理第三方配置缓存。 |
cache.dictionary.invalidate / dictionary |
按 group 清理字典/配置类缓存。 |
cache.redis_object.invalidate / 任意 Redis namespace |
按 keys 删除用户、群、关系、权限、未读等 Redis 对象缓存。 |
cache.group_hot_messages.invalidate / group_hot_messages |
按 topic 和可选 ranges 清理群热消息缓存。 |
11.11 IM Server 已接入的死信处理闭环
IM Server 已将死信从“只写表”补齐为“可查询、可忽略、可解决、可重放”的处理闭环。Admin 不直连租户库,列表和详情通过 IM 内部只读 API 查询;写操作仍通过 im.admin.command 投递给 IM。
Admin 查询接口:
| 接口 | 说明 |
|---|---|
GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters?status=dead&limit=50&cursor= |
死信分页列表,支持 status/biz_type/biz_id/event_type 过滤。 |
GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters/{dead_id} |
单条死信详情,包含原始 Kafka envelope。 |
Admin 处理命令:
event_type |
说明 | Payload 关键字段 |
|---|---|---|
kafka.dead_letter.replay |
删除原消费日志并按原 source_topic 重投 envelope,成功后标记 replayed。 |
dead_id、actor_user_id、note |
kafka.dead_letter.ignore |
人工确认无需处理,标记 ignored。 |
dead_id、actor_user_id、note |
kafka.dead_letter.resolve |
线下修复后标记 resolved。 |
dead_id、actor_user_id、note |
重放规则:
- IM 先把
im_kafka_dead_letters.status改为pending_replay,并删除对应im_kafka_consume_log。 - IM 按死信记录中的
source_topic重新投递原 envelope。 - 投递成功后把死信标记为
replayed,并累计replay_count。 - 如果重放后仍失败,普通消费者会再次写入新的 dead 记录,保留完整处理痕迹。
11.12 IM Server 已接入的用户治理命令
IM Server 已在 im.admin.command 中接入用户治理类后台动作。Admin 侧写 hotgo 操作单并发送 Kafka Command,IM 消费后修改租户库用户状态、踢在线连接、清理缓存并回传 im.admin.command.result。
支持命令:
event_type |
行为 | Payload 关键字段 |
|---|---|---|
user.ban |
封禁用户,设置 StateSuspended 并踢所有在线会话。 |
actor_user_id、user_id 或 target_user_id、reason |
user.unban |
解封用户,设置 StateOK。 |
actor_user_id、user_id 或 target_user_id、reason |
user.freeze |
冻结账号,设置 StateSuspended 并踢所有在线会话。 |
actor_user_id、user_id 或 target_user_id、reason |
user.unfreeze |
恢复账号,设置 StateOK。 |
actor_user_id、user_id 或 target_user_id、reason |
user.device.kick |
踢指定设备在线连接,并删除设备推送记录。 | actor_user_id、user_id 或 target_user_id、device_id、reason |
user.logout_all |
踢用户所有在线连接,不修改账号状态。 | actor_user_id、user_id 或 target_user_id、reason |
user.account.cancel |
注销账号,默认软删除;hard=true 时硬删除。 |
actor_user_id、user_id 或 target_user_id、hard、reason |
user.account.delete |
删除账号,按硬删除处理。 | actor_user_id、user_id 或 target_user_id、reason |
命令示例:
{
"event_id": "admin:user:ban:default:usrabc:001",
"tenant_code": "default",
"source_service": "hotgo-admin",
"target_service": "im-server",
"event_type": "user.ban",
"biz_type": "user_governance",
"biz_id": "usrabc",
"idempotency_key": "admin-operation-id",
"occurred_at": "2026-08-26T10:10:00.000Z",
"payload": {
"command_id": "admin-operation-id",
"actor_user_id": "usrAdmin",
"user_id": "usrabc",
"reason": "违规处理"
}
}
处理说明:
-
封禁/冻结共用 Tinode 用户状态
StateSuspended;解封/恢复共用StateOK。 -
状态变更后 IM 会清理用户缓存,并向本节点 Hub 投递用户状态变更。
-
踢设备只影响匹配
device_id的在线连接,同时删除该设备 Push token 记录。 -
全端登出只踢在线连接,不修改用户状态。
-
注销/删除会停止用户相关 Topic、清理缓存并调用
store.Users.Delete。 -
命令执行成功后统一回传
im.admin.command.result,并写入im.audit.event。 -
群消息消费成功后继续写入 Redis 热消息缓存,便于 24 小时内热点拉取。
事件示例:
{
"event_id": "msg.persist.1.grpabc.10086",
"tenant_code": "default",
"source_service": "im-server",
"target_service": "im-server",
"event_type": "message.persist.group",
"biz_type": "message",
"biz_id": "grpabc:10086",
"idempotency_key": "client-mid-001",
"version": 10086,
"occurred_at": "2026-08-26T10:10:00.000Z",
"actor": {
"type": "user",
"id": "usr..."
},
"payload": {
"message_kind": "group",
"read_by_sender": true,
"attachment_urls": [],
"message": {
"SeqId": 10086,
"Topic": "grpabc",
"From": "abcdef",
"Head": {
"x-im-client-mid": "client-mid-001"
},
"Content": {
"txt": "hello"
}
}
}
}
11.9 IM Server 已接入的 Kafka Outbox Worker
IM Server 已接入租户库 im_kafka_outbox 通用发送 worker,用于避免“业务库写成功但 Kafka 未发送”的事件丢失。
当前能力:
- 启动后按 active 租户扫描每个租户库的
im_kafka_outbox。 - 扫描
pending/failed且已到next_retry_at的事件。 - 发送成功后标记
sent、写入sent_at。 - 发送失败后标记
failed,按 Kafka 配置的retry_backoff_ms写入下一次重试时间。 - 首个接入方为
im.message.persist,后续文件生命周期、工作台事件、审计事件可复用同一 outbox。
本轮暂不实现 im.message.push 离线 Push Kafka 化,已记录到 IM 第一至十一章后续增强项。
11.10 IM Server 已接入的文件生命周期事件
IM Server 已接入 im.file.lifecycle 的首批生产与消费,用于把文件上传、附件绑定、群文件登记/删除纳入可靠事件链路。
当前事件:
event_type |
生产时机 | 消费行为 |
|---|---|---|
file.upload.completed |
HTTP/gRPC 上传成功并完成文件记录后。 | 当前作为可靠生命周期事实记录,文件安全任务仍由上传完成事务创建。 |
file.attachments.bind |
im.message.persist 消费成功后,如果消息携带附件。 |
私聊写 im_filemsglinks;群聊写 im_group_files。 |
group.file.created |
群文件 API 登记成功后。 | 当前作为可靠生命周期事实记录,供后续 Admin 摘要、审计、回收策略复用。 |
group.file.deleted |
群文件 API 删除成功后。 | 当前作为可靠生命周期事实记录,供后续 Admin 摘要、审计、回收策略复用。 |
说明:
- Kafka 开启时,消息附件绑定从消息落库消费者中拆出,改由
im.file.lifecycle消费者完成。 - Kafka 关闭时,仍沿用原同步绑定路径,兼容本地环境。
- 文件对象字节流仍走 OSS/文件服务,Kafka 不承载上传/下载内容。
- 合规删除、临时文件 TTL 回收、OSS 删除动作仍保留为后续深化项。
11.11 IM Server 已接入的工作台事件
IM Server 已接入 im.workbench.event 的首批生产与消费。
当前生产范围:
| 模块 | 事件 |
|---|---|
| 会议 | meeting.created、meeting.updated、meeting.canceled、meeting.reminded、meeting.response.updated |
| 日报 | daily_report.draft.created、daily_report.draft.saved、daily_report.submitted、daily_report.reminded |
| 月报 | monthly_report.draft.created、monthly_report.draft.saved、monthly_report.submitted、monthly_report.reopened |
| 总经理信箱 | mailbox.submitted |
当前消费行为:
- 消费
im.workbench.event。 - 按
target_user_ids生成系统通知。 - 写入
im_business_events_outbox,由现有 WS 业务事件投递器下发在线客户端。 - 使用
im_kafka_consume_log做消费幂等,失败按统一重试和死信规则处理。
11.12 IM Server 已接入的审计事件
IM Server 已接入 im.audit.event 的可靠生产,审计事件写入租户库 im_kafka_outbox 后由 Kafka Outbox worker 发送。
当前审计范围:
| 类型 | 事件 |
|---|---|
| 工作台 | 会议、日报、月报、总经理信箱的状态变更。 |
| Admin Command | 后台群治理命令执行成功。 |
| Kafka 死信 | kafka.dead_letter.created。 |
| 文件生命周期 | 上传完成、附件绑定、群文件新增/删除。 |
审计事件当前不在 IM 内部落审计表,预期由 Admin/HotGo 消费 im.audit.event 后写入 hotgo 审计库或审计表。
12. 风险与约束
- Kafka 是异步链路,Admin 后台命令不能默认立即成功,只能显示
pending/running/succeeded/failed。 - 查询不能通过 Kafka 常规化实现,高频实时查询必须走内部只读 API 或 hotgo 摘要表。
- 内部 API 不能暴露公网,签名、nonce、防重放和审计必须首批完成。
- 配置事件可能丢失或延迟,IM 必须保留启动预热和缓存缺失时的 Admin 配置查询兜底。
- Kafka 消费可能重复,所有消费者和业务写入都必须幂等。
- Admin 和 IM 不跨库直连后,排障要依赖
trace_id串联 hotgo 操作单、Kafka 事件、IM 执行日志和 WS 下发日志。