Files
im_server/docs/IM-工作台服务拆分迁移稳定方案.md
T

21 KiB
Raw Blame History

IM 工作台服务拆分迁移稳定方案

目标:将 im_server 中的工作台业务拆到独立仓库 /Users/youshunlai/go_workspace/im_workbench,先形成独立服务和独立发布边界;数据库暂不迁移,等接口全部稳定后再单独做工作台库迁移。

1. 结论

建议采用“独立仓库 + 分阶段迁移 + Nginx 分流 + Redis 统一 Token 鉴权”的方式:

  1. im_server 保留 IM 核心能力:登录态校验、聊天协议、单聊/群聊、消息存储、长连接、在线状态、Redis 实时 fanout、Kafka 消息消费。
  2. im_workbench 承接工作台能力:工作台入口、组织架构、考勤、会议、会议笔记、日报、月报、总经理信箱、工作台红点。
  3. 当前阶段不迁移数据库,im_workbench 先连接现有租户库读取和写入工作台相关表。
  4. 等 im_workbench 接口、鉴权、Kafka、监控全部稳定后,再单独启动数据库迁移专项。
  5. 工作台接口直接切换为 /v1/workbench/*,由 Nginx/API Gateway 按路径分发到 im_workbench;im_server 不再代理工作台请求。
  6. 迁移采用“先复制迁出、旧代码保留”的方式;只有 im_workbench 全部接口、数据库、Kafka、监控联调通过后,才删除 im_server 中的工作台旧代码。

一句话:网关分流,Redis 鉴权,先拆服务和接口,数据库后迁,最后清理 im_server 中的工作台旧代码。

2. 拆分范围

2.1 迁出到 im_workbench

以下业务从 im_server 迁移到 im_workbench:

模块 当前接口示例 迁移归属
工作台入口 GET /v1/workbench im_workbench
组织架构 /v1/workbench/organization/* im_workbench
考勤打卡 /v1/workbench/attendance/* im_workbench
会议通知 /v1/workbench/meetings* im_workbench
会议笔记 /v1/workbench/meeting-notes、/v1/workbench/meetings/{id}/notes* im_workbench
日报 /v1/workbench/reports/daily* im_workbench
月报 /v1/workbench/reports/monthly* im_workbench
总经理信箱 /v1/workbench/mailbox* im_workbench
工作台红点 workbench_total_unread、模块未读 im_workbench 计算,im_server 聚合透出

2.2 留在 im_server

以下能力继续留在 im_server:

模块 说明
登录与 Token 签发 C 端登录仍由 IM 负责,登录成功后把 Token 会话写入 Redis。
聊天消息 单聊、群聊、消息 seq、消息落库、Redis fanout、WS 下发。
群治理 群资料、群成员、禁言、进群审批、群公告、群文件。
文件上传入口 POST /v0/file/u/ 暂留 im_server,工作台只保存业务引用;后续可单独拆文件服务。
系统通知投递 最终给 C 端的在线通知仍由 im_server 通过 WS 下发。
Kafka 消费到 C 端 Admin 或 Workbench 产生的业务事件,由 im_server 消费后找在线用户下发。

3. 目标架构

C 端 App
  |
  | HTTPS
  v
Nginx / API Gateway
  |-- /v1/im/*、/v0/channels、/v0/file/u/ -> im_server
  |-- /v1/workbench/* -> im_workbench

im_workbench
  |
  | MySQL
  v
现有租户库中的工作台表

im_workbench
  |
  | Kafka: workbench 业务事件
  v
im_server
  |
  | WebSocket
  v
C 端在线用户

稳定边界:

  1. im_workbench 不直接操作 IM 消息表、在线连接、WebSocket。
  2. im_server 不直接写工作台业务表,也不代理工作台 HTTP 请求。
  3. im_server 登录成功后将 Token 会话写入 Redis,im_workbench 每次请求从 Redis 校验 Token。
  4. 两边共享用户 ID 口径:内部管理和工作台接口使用数据库 BIGINT/int64;聊天协议层仍可使用 usr...。
  5. 工作台需要通知 C 端时投 Kafka 事件,由 im_server 统一下发。

4. 数据库处理方案

4.0 当前阶段原则

当前阶段数据库先不迁移。im_workbench 独立服务启动后,仍访问现有租户库中的工作台相关表,先保证新服务接口、Redis Token 鉴权、Nginx 路由、Kafka 事件和监控全部跑通。

代码层面禁止服务启动时自动建库建表。im_server 和 im_workbench 都只负责连接已有数据库和执行正常业务 SQL;所有 CREATE DATABASE、CREATE TABLE、ALTER TABLE、索引变更、初始化字典和种子数据,统一整理为 SQL 文件,经审核后由人工或运维发布流程执行。

当前 MySQL adapter 已禁用 CreateDb 自动建库建表和 UpgradeDb 自动升级 DDL。服务启动时如果数据库不存在会直接连接失败;数据库版本不一致时只报错提示执行人工 SQL,不再自动补表、补字段或补索引。

数据库迁移作为后置专项处理,触发条件是:

  1. /v1/workbench/* 全部接口在本地、测试环境、前端联调中稳定。
  2. im_server 中工作台旧接口确认没有客户端继续访问。
  3. 工作台事件、通知、红点、文件引用链路全部验收通过。
  4. 运维确认新库命名、备份、监控、账号权限和迁移窗口。

4.1 当前工作台相关表

当前这些表仍保留在租户库 tinode 中,暂不迁移;后续数据库专项时再纳入迁移范围:

表 用途 迁移建议
im_workbench_apps 工作台模块配置、排序、入口 暂留租户库;后续迁到工作台库或由 Admin 配置后同步。
im_org_departments 组织部门树 暂留租户库;后续迁到工作台库。
im_org_members 组织成员、岗位、联系方式 暂留租户库;保留 user_id BIGINT 关联 IM 用户。
im_org_visibility_overrides 组织可见范围覆盖 暂留租户库。
im_attendance_rules 考勤规则 暂留租户库。
im_attendance_shifts 考勤班次 暂留租户库。
im_attendance_records 考勤日记录 暂留租户库。
im_attendance_checkins 打卡流水 暂留租户库。
im_attendance_appeals 补卡/异常申诉 暂留租户库。
im_meetings 会议主表 暂留租户库。
im_meeting_participants 会议参与人 暂留租户库。
im_meeting_note_files 会议笔记文件引用 暂留租户库;文件实体仍在文件表/OSS。
im_daily_reports 日报 暂留租户库。
im_monthly_reports 月报 暂留租户库。
im_mailbox_config 总经理信箱配置 暂留租户库;后续可迁到工作台库或 Admin 公共库配置后同步。
im_mailbox_submissions 总经理信箱提交 暂留租户库。

4.2 不迁出的表

表/数据 原因
users、认证、会话、设备 属于 IM 登录和账号基础能力。
topics、subscriptions、messages、im_group_messages 属于聊天主链路。
好友、黑名单、群成员、群治理表 属于 IM 社交关系和群聊能力。
文件实体表、OSS 元数据 暂不迁移,工作台只保存 file_id 引用。
im_system_notifications 建议先留在 im_server,作为统一 C 端通知中心。后续如要拆,可单独设计通知服务。
im_business_events_outbox 已废弃,不纳入迁移;当前代码不再自动建表,工作台拆分后事件统一走 Kafka 或实时投递链路。

4.3 后续目标库建议

后续接口稳定后,再给工作台单独库,命名按环境区分:

本地:im_workbench
测试:im_workbench_test 或按运维规范命名
生产:im_workbench

如果继续保持“一个租户一个库”,则工作台也应支持租户库路由:

hotgo.im_tenant
  - code
  - im_db_name
  - workbench_db_name
  - im_internal_base_url
  - workbench_internal_base_url

正式方向建议在 hotgo.im_tenant 或租户配置表里增加工作台库配置,而不是让 im_server 读取工作台库 DSN。

4.4 后续数据迁移顺序

迁库等接口全部稳定后再做,不要和本轮服务拆分混在一起。后续建议分四步:

  1. 建新库新表:在 im_workbench 库手动执行审核后的 SQL,创建同结构表,先不接线上流量。
  2. 全量复制:从租户库复制历史工作台表数据到工作台库。
  3. 增量同步:迁移窗口内对工作台写操作做双写,或由旧库 binlog/脚本同步到新库。
  4. 切读切写:先灰度读新库,再切写新库,最后关闭旧库工作台写入。

迁移校验至少包括:

校验项 说明
行数校验 每张表按租户统计行数。
主键/业务唯一键校验 meeting_id、report_id、submission_id、department_id 等必须一致。
更新时间校验 对比 updated_at 最大值,确认增量无遗漏。
抽样内容校验 抽样会议、日报、月报、考勤记录详情。
外键引用校验 会议参与人、会议笔记文件、组织成员部门引用不能断。

4.5 回滚原则

当前阶段没有数据库迁移,回滚以服务和路由为主。后续真正迁库时,旧库数据至少保留一个发版周期,不删除旧表。

回滚策略:

  1. 如果服务拆分异常:关闭 /v1/workbench/* 路由,前端回退旧路径或旧版本。
  2. 如果 im_workbench 业务异常:保留 im_server 旧工作台代码作为临时回退能力。
  3. 如果 Redis 鉴权异常:停止工作台新路由,优先恢复登录态校验。
  4. 如果 Kafka 通知异常:业务写入不回滚,只降级为客户端主动拉取和后台补偿推送。
  5. 后续如果新库读写异常:再按迁库专项方案切回旧库。

5. 路由与鉴权迁移方案

5.1 Nginx 分流规则

工作台接口直接使用新路径 /v1/workbench/*,Nginx/API Gateway 按路径分流:

/v1/im/*              -> im_server
/v0/channels          -> im_server
/v0/file/u/           -> im_server
/v1/workbench/*       -> im_workbench

建议工作台目标路由:

原 IM 路径 新 Workbench 路径
GET /v1/im/workbench GET /v1/workbench
/v1/im/organization/* /v1/workbench/organization/*
/v1/im/attendance/* /v1/workbench/attendance/*
/v1/im/meetings* /v1/workbench/meetings*
/v1/im/meeting-notes* /v1/workbench/meeting-notes*
/v1/im/meetings/{id}/notes* /v1/workbench/meetings/{id}/notes*
/v1/im/reports/daily* /v1/workbench/reports/daily*
/v1/im/reports/monthly* /v1/workbench/reports/monthly*
/v1/im/mailbox* /v1/workbench/mailbox*

不建议让 im_server 继续代理工作台请求。路由由 Nginx 分流,服务各自鉴权、各自访问自己的数据库。

5.2 Redis Token 鉴权

登录仍由 im_server 负责。登录成功后,im_server 将 Token 会话写入 Redis,im_workbench 直接从 Redis 校验登录态。

Redis Key 建议:

im:session:token:{token_hash}

Value 建议使用 JSON:

{
  "user_id": 42,
  "tenant_id": 1,
  "tenant_code": "default",
  "device_id": "ios-device-1",
  "auth_level": "auth",
  "state": "active",
  "expires_at": "2026-09-13T18:00:00+08:00",
  "issued_at": "2026-09-13T10:00:00+08:00"
}

请求 Header 保持 C 端统一:

Header 说明
Authorization Bearer {token},两个服务都从 Redis 校验。
X-MeetingIM-APIKey 应用 API Key。
X-Tenant-Code 租户编码。
X-IM-Schema 协议版本。
X-Request-ID 链路追踪 ID。

校验流程:

  1. 从 Authorization 取 Bearer Token,先做格式校验。
  2. 对 Token 做 hash 后查 Redis,避免 Redis 明文 Key 暴露原始 Token。
  3. 校验会话存在、未过期、state=active。
  4. 校验 X-Tenant-Code 与 Redis 会话中的 tenant_code 一致。
  5. 校验 API Key 是否可用于当前租户和客户端类型。
  6. 将 tenant_id、tenant_code、user_id、device_id 写入请求上下文。

Token 失效场景:

场景 处理
用户主动退出 删除当前 Token Key。
踢设备 删除指定 device_id 对应 Token Key。
冻结/封禁账号 删除用户全部 Token Key,并通过 Kafka 通知在线连接断开。
修改密码 删除用户全部 Token Key。
Token 过期 Redis TTL 自动过期。

5.3 共享鉴权代码

建议抽一个轻量公共包,两个服务复用:

meetingim_common/auth/session

公共包只放稳定协议:

  1. Redis Key 生成。
  2. Token hash 规则。
  3. Session JSON 结构。
  4. Header 解析。
  5. 错误码定义。

不要把数据库访问、HTTP handler、业务权限放进公共包,避免公共包变成新的耦合点。

6. 事件与通知

工作台拆出后,工作台业务变更不再直接调用 im_server 代码函数,而是投 Kafka。

建议 Topic:

Topic 生产者 消费者 用途
im.workbench.event im_workbench im_server 工作台事件、红点、系统通知、WS 下发。
im.cache.invalidate im_workbench/Admin im_server/im_workbench 组织、工作台模块、配置变更后的缓存失效。
im.audit.event im_workbench Admin/审计消费者 工作台敏感操作审计。

im.workbench.event 消息建议字段:

{
  "event_id": "wb_default_meeting_mtg_1_created_v1",
  "tenant_id": 1,
  "tenant_code": "default",
  "actor_user_id": 42,
  "module": "meeting",
  "action": "meeting.created",
  "resource_id": "mtg_1",
  "target_user_ids": [42, 43],
  "title": "会议通知",
  "summary": "项目周会",
  "data": {
    "meeting_id": "mtg_1",
    "badge_delta": 1,
    "version": 1
  },
  "occurred_at": "2026-09-13T13:30:00+08:00"
}

消费原则:

  1. event_id 全局幂等,消费端记录处理日志。
  2. 系统通知写入失败可重试,超过次数进入死信。
  3. WS 下发失败不影响业务写入,客户端重连后通过工作台接口和 bootstrap 补偿。
  4. 工作台红点以服务端查询为准,Kafka 只做实时增量提醒。

7. 代码迁移步骤

7.1 第一阶段:服务骨架

在 /Users/youshunlai/go_workspace/im_workbench 新建 Go 服务:

im_workbench/
  cmd/workbench/
  internal/http/
  internal/store/
  internal/kafka/
  internal/config/
  docs/
  sql/

第一阶段只做:

  1. 配置文件、日志、MySQL、Redis、Kafka 初始化。
  2. 健康检查:GET /healthz、GET /readyz。
  3. Redis Token 鉴权中间件。
  4. 租户上下文解析。
  5. 复制工作台接口 DTO 和响应结构。

7.2 第二阶段:迁移业务代码

从 im_server 迁出以下代码:

当前位置 迁移内容
server/im_http.go serveIMWorkbench、Organization、Attendance、Meetings、MeetingNotes、DailyReports、MonthlyReports、Mailbox 相关 handler。
server/store/store.go 工作台相关 Persistence Interface 和 mapper。
server/store/types/*.go workbench.go、organization.go、attendance.go、meeting.go、reports.go、mailbox.go 等类型。
server/db/mysql/tenant_adapter.go 工作台相关 SQL 实现。
server/im_business_events.go 工作台事件生产逻辑拆到 im_workbench,只保留 IM 消费下发。
server/kafka_workbench_audit.go 工作台事件生产侧迁出,IM 侧保留消费或下发处理。

迁移时要保留测试:

  1. 原 handler 单测迁到 im_workbench。
  2. im_server 原有工作台测试暂时保留,作为旧链路回归保护。
  3. im_workbench 新增同等接口测试,确认新路径行为一致。
  4. Kafka 事件用例拆为 Workbench 生产端测试和 IM 消费端测试。

7.3 第三阶段:Nginx 路由切换

im_server 中原工作台 handler 暂时不删除,不做主链路代理。新请求由 Nginx/API Gateway 直接分流到 im_workbench:

客户端请求 /v1/workbench
  -> Nginx/API Gateway
  -> im_workbench Redis Token 鉴权
  -> return response

路由层必须支持:

  1. /v1/workbench/* 全量转发到 im_workbench。
  2. /v1/im/*、/v0/channels、/v0/file/u/ 继续转发到 im_server。
  3. 透传 Authorization、X-MeetingIM-APIKey、X-Tenant-Code、X-IM-Schema、X-Request-ID。
  4. 工作台服务超时建议 3 到 5 秒。
  5. 工作台不可用时返回明确 WORKBENCH_UNAVAILABLE 或网关 502/504,不影响聊天主链路。

旧代码保留策略:

  1. im_server 工作台旧 handler、store、SQL 先保留。
  2. 前端切新路径后,测试环境持续观察一个发版周期。
  3. im_workbench 全部接口、数据库、Kafka、监控确认稳定后,再从 im_server 删除旧工作台代码。
  4. 删除前要确保没有客户端继续访问 /v1/im/workbench、/v1/im/meetings* 等旧路径。

7.4 第四阶段:接口稳定验收

当前阶段只做接口稳定验收,不做数据库切换。建议保留以下服务开关:

配置 说明
workbench.service.enabled 是否启用工作台独立服务。
workbench.route.enabled Nginx/API Gateway 是否启用 /v1/workbench/* 路由。
workbench.kafka.enabled 是否启用工作台事件 Kafka。

后续迁库专项再增加数据库切换开关:

workbench.db.read_from=old|new|shadow
workbench.db.write_to=old|dual|new

8. 发布与回归

8.1 发布前检查

  1. im_workbench 健康检查通过。
  2. im_workbench 可连接现有租户库,并只访问工作台相关表。
  3. Redis Token 鉴权与 im_server 登录态一致。
  4. Nginx/API Gateway 工作台路由灰度开关默认关闭或仅测试环境开放。
  5. Kafka Topic、消费组、死信队列已建。
  6. 监控已覆盖 HTTP 成功率、耗时、Kafka 积压、DB 错误、网关转发失败。
  7. 本阶段不要求新工作台库建表和数据复制。

8.2 必测用例

模块 用例
工作台入口 登录后返回模块排序、开关、未读角标。
组织架构 普通用户、手机号注册用户、邀请用户、测试账号可见范围正确。
考勤 配置查询、打卡、重复幂等、记录列表、申诉创建。
会议 创建、列表、详情、修改、取消、提醒、参会响应。
会议笔记 上传文件、绑定文件、详情、替换、删除、权限校验。
日报 创建草稿、保存、提交、列表、统计、提醒。
月报 创建、保存、提交、reopen、详情、统计。
总经理信箱 配置查询、匿名/实名提交、附件引用。
通知 工作台事件生成系统通知,在线用户收到 WS,离线用户重登可补偿。
回滚 关闭 /v1/workbench/* 路由后,前端回退旧版本或网关明确降级;旧代码未删前可切回旧路径。

9. 风险点

风险 处理
一次性拆代码和迁库导致回归面过大 本阶段不迁库,只拆服务和接口;数据库等接口稳定后单独迁移。
工作台依赖 IM 用户资料 通过 IM 内部查询 API 获取用户摘要,禁止直连 IM 库。
IM 需要工作台红点 通过工作台内部接口或 Kafka 事件聚合,禁止 im_server 查工作台库。
文件上传仍在 im_server 工作台保存 file_id 引用,文件服务后续再独立拆。
Kafka 事件丢失 生产端直接投 Kafka,必要时使用业务失败表补偿;消费端使用幂等和死信队列。
前端路径切换风险 前端切到 /v1/workbench/* 前必须完成接口对照、灰度和回归。
数据不一致 当前阶段继续读写同一套租户库,避免双写不一致;后续迁库再做影子读和校验。

10. 推荐落地顺序

  1. 在 im_workbench 建服务骨架、配置、健康检查、Redis Token 鉴权。
  2. 迁移工作台类型、store interface、MySQL 实现和单测。
  3. 迁移 /v1/workbench、组织架构、考勤接口。
  4. 迁移会议、会议笔记、日报、月报、总经理信箱接口。
  5. Nginx/API Gateway 增加 /v1/workbench/* 路由,默认关闭或只对测试环境开放。
  6. 本地环境用新路径跑通全部工作台接口。
  7. 测试环境启用新路由灰度,只切少量测试账号。
  8. 全量接口稳定后,确认没有客户端访问旧 /v1/im/* 工作台路径。
  9. 清理 im_server 内工作台业务实现,只保留 IM 主链路。
  10. 单独启动数据库迁移专项,新建工作台库、复制数据、影子读、切读写。

11. 当前建议先做的第一步

第一步不要急着删 im_server 代码,建议先在 im_workbench 完成最小可运行服务:

  1. 监听端口建议:7070。
  2. 支持 GET /healthz、GET /readyz。
  3. 支持 Redis Token 鉴权和统一 Header 校验。
  4. 接入本地 MySQL、Redis、Kafka 配置。
  5. 先迁 GET /v1/workbench 一个读接口做样板。
  6. Nginx/API Gateway 加 /v1/workbench 测试路由,只开放样板接口。

这样第一步上线风险最低,也能验证服务拆分、Redis 鉴权、链路追踪、配置和部署是否顺畅。