Chatto横向扩展与高可用架构完整指南:多进程+NATS集群实现无状态水平扩展
【免费下载链接】chattoA fully-featured team and group chat application that you can easily selfhost.项目地址: https://gitcode.com/gh_mirrors/chatt/chatto
Chatto是一个可自托管的全功能团队与群聊应用(团队聊天、群组聊天、语音、附件齐全)。它的最大架构特点是:把NATS JetStream 集群作为唯一持久化数据源,应用进程本身完全无状态,因此天然支持多进程横向扩展与高可用部署——加一台机器就能扩容,摘掉一台机器也不掉线。
为什么 Chatto 能"加机器就扩容"
大多数聊天系统要水平扩展,得先处理数据库主从、会话粘滞、消息队列这些麻烦。Chatto 走了一条更直接的路(见 ADR-002 与 ADR-001):
| 设计决策 | 对扩展的意义 |
|---|---|
| 单二进制 + 内嵌 NATS | 小规模时一条命令启动,零依赖;不是限制,只是起点 |
| 外接 NATS 集群 | 应用无论连内嵌还是远程 NATS,都只是普通 NATS 客户端 |
| NATS JetStream 为唯一数据源 | 没有关系数据库,扩容故事是 NATS 原生的(Raft 共识),无需数据库主从 |
| 进程内只做内存投影 | 每个副本从事件流重建读模型,副本之间互不依赖 |
关键结论来自 架构清单:运行时是进程级的,但被设计为多个 Chatto 副本连到同一个 NATS 账号下安全运行——正确性来自 JetStream 和 KV 的原子性,而不是进程内锁或单例协程。
横向扩展第一步:把状态交给外部 NATS 集群
chatto run默认在内嵌 NATS 中运行,适合单机自托管。要横向扩展,只需要把存储层换成外部 NATS(集群版开启 JetStream 即可),Chatto 进程随即变成无状态服务:
- 事件日志
EVT:所有持久化领域事实(消息、房间、成员、权限、资产……)以 protobuf 事件追加写入,用 JetStream 乐观并发控制(OCC)防冲突; RUNTIME_STATEKV:跨副本共享的最新值状态(凭证、加密包装、通知边界等),KV 的原子写保证多副本并发安全;MEMORY_CACHEKV:内存级易失共享记录(存活状态、租约、计数器),节点丢失不丢数据;- 实时推送:JetStream 把已提交事实重发布到
live.evt.>,各副本本地投影跟上后才推给客户端。
资源全景(流、KV、对象存储、消费者清单)见 NATS 资源清单,事件与主题命名见 主题与事件清单。
多副本一致性:事件溯源 + 内存投影
多个副本同时读写时,Chatto 靠三件事保证不出错:
- 写路径:追加即提交。写操作向
EVT追加带 OCC 守卫的事件,冲突会被 JetStream 直接拒绝并提示重试,不会出现"两个副本各改一半"的状态(授权与聚合 OCC 设计见 ADR-087)。 - 读路径:本地投影追平才服务。每个副本从
EVT重建内存投影,写者会等待相关投影序列跟上,实现"读己之写"。副本重启或新扩容进来,冷启动只是"重放一遍事件",不需要迁移数据。 - 断连自愈:NATS 恢复门。副本在 NATS 连接出现空档时会把自己标记为不健康、隔离实时会话、恢复易失资源、刷新 KV 监听并等待投影追平;恢复停滞 5 分钟后才触发存活探针失败(见 运行时组件清单 中的 "NATS recovery gate" 条目)。
后台任务多副本共享:持久消费者 + 选举
视频转码、资产清理、推送投递、密钥销毁等后台工作不是"每台机器干一份",而是所有副本共享同一个 JetStream 持久消费者(如chatto-asset-processing-v1):
- at-least-once 投递 + 幂等处理 + 显式 ack,任务中断会被自动重投;
- 消费者是版本化的持久资源,可缩容到零仍保留进度;
- 需要"全局唯一"的工作(LiveKit 对账、投影快照发布、孤儿清理)通过 KV 租约选举出当前负责人,副本挂掉后租约过期自动转移。
可选能力(搜索、导出器、资产处理)以运行时单元目录注册在 cli/cmd/run.go,内嵌单元失败只降级该能力并由监督者按指数退避(上限 30 秒)重启,不会拖垮整个服务进程。
实战部署:K8s 三副本高可用清单
仓库内置了可直接使用的 Kubernetes 示例(examples/k8s/README.md):
- nats.yaml:NATS 以StatefulSet运行,
--jetstream+ 持久卷,数据不出集群; - chatto.yaml:Chatto 以Deployment运行,
replicas: 3,三个副本对 Ingress 完全等价; - 滚动更新策略
maxUnavailable: 0+maxSurge: 1,配合/readyz就绪探针与/healthz存活探针,实现零停机升级。
核心操作只有几条命令(摘自 K8s 示例说明):
# 查看副本状态与日志 kubectl -n chatto get pods kubectl -n chatto logs -f deployment/chatto # 扩容到 5 个副本:新副本自动重放 EVT 投影后接入流量 kubectl -n chatto scale deployment/chatto --replicas=5 # 零停机滚动重启 kubectl -n chatto rollout restart deployment/chatto由于副本无状态,扩容 = 加 Pod,缩容 = 删 Pod,不存在会话粘滞和数据迁移。
扩展方案对照清单
| 规模 | 推荐形态 | 说明 |
|---|---|---|
| 个人 / 小团队 | 单二进制 + 内嵌 NATS | chatto run一条命令,数据目录备份即可 |
| 中型团队 | 1 个 NATS + N 个 Chatto 副本 | 本指南的主场景,K8s 示例直接可用 |
| 大型 / 多机房 | NATS 集群(JetStream Raft)+ N 副本 | 存储层自身高可用,"扩容故事是 NATS 原生的"(ADR-001) |
小结
- Chatto 的无状态设计让多进程水平扩展成为"连接数"问题而不是"数据分片"问题;
- 一致性由JetStream OCC 写入 + 内存投影追平 + KV 原子共享状态共同保证;
- 后台任务用共享持久消费者 + 租约选举在多副本间协作与容错;
- 想动手体验,直接从 examples/k8s/ 的三副本清单开始,或阅读 ADR-002 了解"内嵌只是起点,外接集群才是扩展位"的设计动机。
【免费下载链接】chattoA fully-featured team and group chat application that you can easily selfhost.项目地址: https://gitcode.com/gh_mirrors/chatt/chatto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考