目录

Rocket.Chat:面向任务关键型组织的自托管 CommsOS 架构拆解

目录

项目定位

Rocket.Chat 不是"又一个 Slack 克隆"。仓库 README 把产品定位写得很直白——“The Secure CommsOS™ for mission-critical operations”,并把 Deutsche Bahn、The US Navy、Credit Suisse 列为典型客户。当一家客户的核心需求是"通信数据不能离开自己的网络、必须可审计、可联邦、可被本国监管框架接受"时,它要解决的就不再是"消息能不能送达",而是一整套"通信操作系统"的命题:自托管、气隙部署、端到端加密、跨组织联邦、模块化扩展、企业级 SSO/SAML/LDAP。本文关注的不是 README 上的功能清单,而是仓库里那一百多个 TypeScript 包、微服务拆分边界和 Apps-Engine 这层扩展框架是怎么把这些需求落地的。

学习目标

读完本文,你应该能够:

  • 识别 Rocket.Chat monorepo 的四层结构(主应用 / 微服务 / UI 包 / Apps-Engine),并说明每层承担的职责
  • 解释 DDP 协议 + MongoDB Oplog 在消息订阅推送链路中的角色,以及单节点 Mongo 会带来什么退化
  • 描述 Apps-Engine 的沙箱模型与钩子链,判断一个集成需求应该写 App 还是 fork 主应用
  • 区分 NATS/moleculer 私有事件与公开 REST API 的边界,避免自接入服务时反向 hack
  • 评估联邦、E2EE、合规审计三项能力对部署姿态的影响,并给出 PoC 前的检查清单

项目位置

RocketChat/Rocket.Chat 是 GitHub 上规模最大的自托管团队通信仓库之一(以下数据统计时点为 2026-06-17,Stars/Forks 与 Release 版本会随后续发布变动):

指标数值
Stars / Forks45,503 / 13,656
主要语言TypeScript
包管理Yarn 4.12.0(Berry workspaces)
构建编排Turborepo
Node 运行时22.22.3
协议MIT(apps/meteor/ee/ee/ 下为独立 EE 许可证)
最近 Release8.5.1(2026-06-15)
仓库组织monorepo:apps/* + packages/* + ee/apps/* + ee/packages/*

要理解这个仓库的形状,先抛掉"Rocket.Chat 是一个 Meteor 应用"的旧印象。apps/meteor 仍是承担实时消息主链路的核心应用,但它已经不是孤岛——apps/ 旁还有 uikit-playgroundpackages/ 里有 50 多个 TS 包,ee/ 下另有一套企业版二进制和微服务。

系统地图

仓库根目录的 package.json 直接声明了 workspace 范围(apps/*packages/*ee/apps/*ee/packages/*),并把 turbo.json 作为构建与测试编排入口。这套结构对应的不只是工程便利,而是 Rocket.Chat “主应用 + 微服务 + UI 包 + 扩展框架"四件套的产品布局:

┌─────────────────────────────────────────────────────────────────────┐
│                 apps/meteor  (主应用,消息主链路)                     │
│  - Meteor + React + TypeScript                                       │
│  - DDP 协议、WebSocket、消息存储、订阅推送                             │
│  - Apps-Engine 宿主                                                   │
└──────────────┬─────────────────┬────────────────────┬───────────────┘
               │                 │                    │
   ┌───────────▼──────┐  ┌───────▼──────┐  ┌───────────▼──────────┐
   │  Microservices   │  │  UI 工具包    │  │   扩展框架           │
   │  (ee/apps/)      │  │ (packages/)  │  │  (packages/          │
   │                  │  │              │  │   apps-engine)       │
   │ - authorization  │  │ - fuselage   │  │                       │
   │ - account        │  │ - ui-kit     │  │ 第三方 App 加载       │
   │ - presence       │  │ - ui-video   │  │ (Apps-Engine)         │
   │ - omnichannel    │  │ - ui-voip    │  │                       │
   │                  │  │ - ui-composer│  │                       │
   │ 通信:NATS /     │  │ - gazzodown  │  │                       │
   │ moleculer        │  │ - i18n       │  │                       │
   └─────────┬────────┘  └──────────────┘  └───────────────────────┘
   ┌─────────▼──────────────────────────────────────────────────────┐
   │  数据层                                                          │
   │  - MongoDB(必须 replica set,使用 Oplog 做 change streams)      │
   │  - 文件存储:本地 FS / AWS S3 + CDN                               │
   │  - 实时事件总线:NATS(TRANSPORTER 环境变量切换)                   │
   └─────────────────────────────────────────────────────────────────┘

读这张图要锁定四件事,否则后面所有判断都会跑偏:

  1. monorepo 不是简单聚合,而是分层apps/meteor 仍是核心后端,承担实时消息、DDP(Distributed Data Protocol,Meteor 自有的发布订阅协议)、WebSocket、订阅推送;packages/ 提供跨边界复用的 UI 组件、HTTP 路由、REST 类型、消息解析器、国际化等;ee/apps/ 是从主应用剥离出来的微服务,承担鉴权、账号、在线状态、客服等独立可扩展的子域;ee/packages/ 则是 EE 二进制配套的私有能力包。
  2. 微服务之间靠 NATS/moleculer 通信,而不是直接 HTTPdocker-compose-ci.yml 里所有微服务都共用同一个 TRANSPORTER 环境变量(缺省 TCP,可换 NATS),并通过 mongo + nats 两个有状态依赖相互连接。这意味着服务间调用走的是 moleculer 的事件总线,而不是 Rocket.Chat 自己暴露的 REST API——这是它在 7.x 之后从单体走向分布式的关键架构选择。
  3. 数据层强依赖 MongoDB 副本集 + Oplogdocker-compose-ci.yml 里的 MONGO_URL 形如 mongodb://mongo:27017/rocketchat?replicaSet=rs0,订阅推送依赖 Oplog;如果部署单节点 Mongo 或没开 replica set,消息实时性会直接劣化。这一点在所有官方部署文档里都是 P0 警告。
  4. Apps-Engine 是独立子框架,不属于 Meteor 应用内部packages/apps-engine 是一个独立的 TypeScript 运行时,允许第三方把"机器人、集成、UI 扩展"打包为 zip 并热加载到主应用里。它是 Rocket.Chat 平台化能力的承载体,也是一道把"主应用代码"和"市场里第三方代码"隔离开的安全边界。

一次消息从发送到订阅

光看结构容易把 Rocket.Chat 误读成"普通的实时聊天后端”。它真正的差异在于"DDP 协议 + Meteor 发布订阅 + Apps-Engine 钩子 + 微服务事件总线"这四件套。下面用"用户 A 在 channel X 发一条文本消息、用户 B 在另一台设备立刻看到"为例,看一次消息在系统里的实际流转(具体类名以仓库当前版本为准,命名遵循 Meteor 约定):

  1. 前端组装消息。客户端(Web / Desktop / Mobile)用 React + Fuselage UI Kit 把消息封装成 DDP 方法调用 sendMessage,连同 channelId、文本、附件、@mention 一并送出。
  2. Meteor 路由与方法执行apps/meteor/server/methods/ 下的 sendMessage 在服务端校验身份(鉴权可能由 authorization-service 微服务在 NATS 上代为校验)、权限(角色 + 权限位)、消息长度与速率限制,然后把消息写入 MongoDB messages 集合,并生成对应 subscriptions 更新。
  3. Oplog 触发订阅推送。Meteor 通过 MongoDB Oplog 监听该 channel 的变更,所有通过 DDP subscriptions 订阅了该 channel 的客户端会收到一条 added/changed 事件;用户 B 的客户端在不动用额外请求的前提下,UI 立刻把消息卡片 push 出来。
  4. Apps-Engine 拦截器触发。消息写入前后,Apps-Engine 会按 App 注册顺序触发 IPreMessageSentPreventIPostMessageSent 等钩子;第三方 Bot 可在这里拦截、改写、转发——这也是 Rocket.Chat 集成的核心扩展点。
  5. 微服务侧异步消化。如果是客服场景,omnichannel 微服务在 NATS 上订阅到 livechat.message 事件后,会更新坐席会话状态、写入 reporting_event,并把这条消息同步到外部 CRM。
  6. 推送与离线补齐。未在线的设备由推送服务(FCM/APNs)经 apps/meteor/server/services/ 收到通知,用户回到客户端后 DDP 自动 reconnect + 增量补齐未确认消息。

理解这条链路再看部署清单,瓶颈就清晰了:MongoDB 的 Oplog 是订阅推送的唯一可靠源(停掉它就退化为定时轮询,延迟和压力都会放大);NATS 是微服务之间唯一可靠的事件通道(不可降级为同步 HTTP,否则背压处理会复杂);Apps-Engine 的执行时间计入消息发送路径,第三方 App 卡住会反过来卡住主链路。

Apps-Engine 的运行时边界

packages/apps-engine 是 Rocket.Chat 把第三方 App 当作一等公民承载的运行时,定义了四类边界:

  • 权限沙箱:每个 App 拥有独立的 Accessors,只能访问显式授权过的 HTTP endpoint、消息流、UI 区域。
  • 声明式 UI 扩展:App 可以声明按钮、模态框、表单字段(基于 ui-kit 包),UI 渲染由主应用代理执行,App 自身只声明 schema。
  • 事件钩子链:消息前/后置、用户加入/离开、room 创建/关闭、Livechat 事件等都有标准钩子;钩子以 await 串行执行,超时会被记入审计日志。
  • 持久化沙箱:App 拥有自己的 KV 存储(AppPersistence),不能直接读写主数据库。

“集成 vs 自研"的判断线就在 Apps-Engine 这一层。如果一个功能能在 App 里用钩子 + UI 扩展实现,就不要去 fork apps/meteor。这是 50+ packages 之所以能稳定演化的核心约束——主应用边界稳定,第三方能力在引擎里横向扩张。

微服务边界:从 monolith 拆出的 4 个核心域

docker-compose-ci.yml 列出了当前 EE 路径下的微服务家族,下面这张表把每个微服务的职责和通信依赖写出来,方便评估"应该接哪个、不该接哪个”:

微服务职责通信依赖适合独立扩展的理由
authorization-service角色、权限、OAuth scope 校验NATS + Mongo权限校验是热点路径,独立扩缩容可降低主应用压力
account-service用户账号 CRUD、跨服务账户合并NATS + Mongo账号域改动频繁,独立部署可让主应用少升级
presence-service在线状态、最后在线时间、跨设备聚合NATS + Mongo状态写多读多,独立成服务便于单独优化
omnichannel-serviceLivechat / 客服会话、坐席分配NATS + Mongo客服业务规则复杂,单独演进不影响主聊天

这些微服务的协议和数据格式都是 NATS/moleculer 私有事件,不是公开 HTTP API。在自部署里接入自己写的服务时,最稳的路径是写一个独立的"适配层"用 moleculer client 接入,不要尝试通过 REST 反向 hack,否则会失去 NATS 的事件回放与背压能力。

联邦、E2EE 与合规的工程实现

Rocket.Chat 把联邦、端到端加密和合规做成了产品级而非"加挂"级的能力:

  • 联邦(Federation):基于 Matrix Federation 协议,允许不同 Rocket.Chat 实例(甚至不同厂商的通信服务)互相发现、加入房间、交换消息。README 直接给出"Enable federation"的开关与文档链接,意味着联邦不再是 Roadmap 项,而是已经可用的生产功能。部署联邦实例需要打开 Federation_Matrix_enabled 等开关,并正确处理 DNS SRV 记录与证书。
  • 端到端加密(E2EE):通过 e2e-keys 和 DDP-S 二进制通道交换密钥,加密消息以加密 blob 落库;服务端无法解密,审计需要从客户端侧获取密钥。E2EE 与合规审计天然冲突——开启后合规日志会失去可读性,这一点在政府/金融场景必须提前与合规团队对齐。
  • 合规与审计:Trust Center 与 Compliance Center 提供 SOC2、ISO27001、GDPR、HIPAA 等合规说明;EE 版提供审计日志、Retention 规则、Legal Hold 等企业级能力。这些能力不是某个隐藏开关,而是 ee/ 目录下的代码与服务,是 Rocket.Chat 敢于叫 “CommsOS” 的工程支撑。

部署路径与陷阱

README 列了四条官方路径,本文不复制,只把"哪个路径对应哪种合规姿态"梳理出来:

  • Docker / Podman / Kubernetes(推荐生产路径):和 docker-compose-ci.yml 几乎一一对应;MongoDB 副本集与 NATS 是必备依赖,K8s 路径还需要为 Oplog 留出磁盘 IO 余量。
  • Launchpad:Rocket.Chat 自家的 Kubernetes 一键脚手架,省去管理每个依赖的成本,适合不想自建 K8s 又想跑生产路径的中小团队。
  • Air-gapped 部署:完全离线网络下的部署模式,对应国防、能源等高敏感场景;离线包需要预先拉取 MongoDB、NATS、Docker 镜像,并通过内网 registry 拉起。
  • Cloud(付费):Rocket.Chat 官方运维的商业云,按 SLA 收费;适合"想用但不想自己运维"的政企客户。

工程上最容易踩的三个坑:

  1. MongoDB 没开 replica set。Oplog 是实时推送的依赖,单节点 Mongo 在压力下会把 DDP 推送退化成定时轮询,CPU 立刻被打满。
  2. 把 Apps-Engine 当成普通 npm 包来 forkpackages/apps-engine 的版本与主 Meteor 应用强绑定,跨版本升级经常需要同步迁移 App schema。
  3. 微服务通信用 REST 反代。失去 NATS/moleculer 的事件回放后,任何"分布式事务"都会退化为补偿逻辑,最终会重写到主应用里。

谁该用、谁可以等

适合选 Rocket.Chat 的场景:

  • 政府、国防、能源、金融等强合规场景,需要通信数据完全自托管、可审计、可气隙;
  • 已有 Kubernetes + MongoDB + NATS 运维能力,需要"统一通信平台 + 客服 + 联邦"三件套;
  • 大型组织(数千至上万坐席),需要 SSO/SAML/LDAP、Retention、Legal Hold、审计日志等 EE 能力;
  • 不想把通信数据放在 SaaS 第三方,但又不愿意投入 Slack/Teams 替代品的从头研发。

可以再等等或换方案的场景:

  • 团队 < 50 人,纯内部沟通,不涉及合规与联邦需求——Mattermost、Element 这类更轻量的方案维护成本更低;
  • 客服场景单一,且已经买了 Zendesk/Intercom——Rocket.Chat 的客服能力对标的是 omnichannel,不一定比专业客服系统更省事;
  • 团队没有 Kubernetes 或 MongoDB 副本集的运维经验——Meteor 主应用在单节点 Mongo 下能跑,但实时性会显著劣化,长期不划算。

采用顺序与决策建议

按团队现状给出推进顺序,避免一步到位踩坑:

  1. 先验证主链路:用 docker-compose-ci.yml 起一套带 MongoDB 副本集 + NATS 的最小集群,跑通 Web 端发消息、订阅推送、文件上传。这一步确认运维基线,不接 EE。
  2. 再评估扩展边界:把第一个集成需求(机器人、外部系统通知、自定义审批流)写成 Apps-Engine App,而不是 fork apps/meteor。如果 App 跑得通,后续 80% 的集成需求都可以走这条路。
  3. 按需启用 EE 微服务:当主应用出现权限校验或在线状态热点时,再引入 authorization-service / presence-service,并提前规划 NATS 集群与 moleculer client 的运维。
  4. 最后处理合规姿态:联邦、E2EE、Legal Hold 这三项必须在合规团队介入后再启用,E2EE 一旦开启不可逆地影响审计日志可读性。

自测题

  • apps/meteor 单节点 + 单节点 Mongo(无副本集)的部署下,用户 B 收到用户 A 消息的延迟特征会发生什么变化?为什么?
  • 一个"消息发出后自动同步到外部 CRM"的集成需求,应该写成 Apps-Engine App 还是 fork 主应用?给出判断依据。
  • 自部署里要接入一个自研的审计服务,直接 REST 调用 omnichannel-service 的接口会有什么后果?正确的接入路径是什么?
  • 开启 E2EE 后,企业合规团队要求保留可读的聊天审计日志,这两项要求能否同时满足?如何对齐?
  • authorization-servicepresence-service 都依赖 NATS + Mongo,如果 NATS 集群不可用,主应用 apps/meteor 还能正常发消息吗?说明调用链。

边界与未覆盖

  • 本文没有展开 packages/fuselage-ui-kit 的具体组件层级、http-routerrest-typings 的生成机制,这些是 UI/类型层细节,需要结合二次开发目标再读。
  • 本文没有涉及 E2EE 的密钥轮换、审计日志与 Retention 规则的合规配置流程——这部分企业落地前必须单独跑一遍 PoC。
  • 本文没有跑 benchmark。仓库没有公开"每秒能处理多少 DDP 订阅"“微服务在 NATS 上的事件吞吐"这类数字,自部署规模评估只能结合自己压测;不要直接套用其它 Meteor 应用的通用经验。

Rocket.Chat 是一份"成熟且边界清晰"的开源答案:主链路稳定、扩展边界明确、企业能力集中在 ee/。剩下的工程取舍在合规姿态、运维能力和微服务规模这三个维度上。