基于NestJS与Angular的可扩展实时协作白板技术架构问询
NestJS + Angular 可扩展实时协作白板架构方案
一、持久连接架构(兼顾性能)
基于NestJS的@nestjs/websockets结合Socket.IO实现持久连接,核心设计如下:
- 依托Socket.IO的自动重连、跨浏览器兼容特性(支持WebSocket/HTTP长轮询 fallback),稳定维持用户长连接
- 采用房间模式:为每个白板文档创建独立Socket房间,用户仅加入对应文档的房间,消息仅在房间内广播,避免全局推送带来的性能损耗
- 分布式场景下,使用Redis Socket.IO Adapter实现NestJS实例集群,多个实例可共享连接状态,用户连接不会因单个实例宕机而中断
Angular端通过ngx-socket-io封装Socket.IO客户端,封装专用Service处理连接、房间加入/退出、消息收发逻辑,保证前端代码的可维护性。
二、数据持久化数据库选型
分两类数据针对性选型:
1. 实时状态数据(光标位置、在线用户、临时编辑状态)
选用Redis:
- 内存级读写性能,适配高频更新的实时数据场景
- 用Hash结构存储每个文档的在线用户列表(key为文档ID,field为用户ID,value为用户资料)
- 用String或Hash存储用户光标坐标(key格式:
cursor:{文档ID}:{用户ID}),支持毫秒级读写
2. 持久化文档数据(白板元素、历史版本)
二选一即可:
- MongoDB:适合存储非结构化的白板元素(如自由图形、文本块),Schema灵活,支持嵌套结构,方便扩展白板元素类型
- PostgreSQL:适合需要事务支持、版本控制的场景,用JSONB类型存储白板元素,结合触发器或自定义函数实现版本快照
三、消息队列的必要性判断
- 中小规模场景:无需独立消息队列,Socket.IO的房间广播+Redis缓存足以处理实时同步,直接在WebSocket Handler中完成数据持久化即可
- 大规模/高并发场景:必须引入消息队列(如RabbitMQ、Redis Stream):
- 解耦实时推送与数据持久化:用户编辑消息先发送至队列,后台异步消费队列完成数据库写入,避免阻塞WebSocket连接
- 处理异步任务:如文档导出、批量版本清理等耗时操作,不占用实时连接资源
- 注:Redis Socket.IO Adapter本身基于Redis Pub/Sub实现跨实例消息同步,属于轻量消息传递机制,可满足基础集群需求,但复杂任务仍需独立队列
四、大规模场景最优架构
前端Angular应用 → Nginx网关(负载均衡+WebSocket代理) → 多节点NestJS集群(Redis Adapter共享连接) ↓ Redis(实时状态缓存) + 主数据库(MongoDB/PostgreSQL) + 消息队列(RabbitMQ/Redis Stream) ↓ 监控系统(Prometheus+Grafana+ELK)
- 前端层:用Service Worker缓存静态资源,减少重复请求;Socket客户端配置自动重连、心跳检测,保证连接稳定性
- 网关层:Nginx做反向代理,配置
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";实现WebSocket代理,同时做负载均衡,自动剔除故障节点 - 应用层:多NestJS实例集群,通过Redis Adapter同步房间、连接状态,每个实例仅处理部分用户连接,避免单节点过载
- 数据层:Redis存实时状态,主数据库存持久化文档,消息队列处理异步任务,实现读写分离、异步解耦
- 监控层:监控WebSocket连接数、消息延迟、数据库QPS,日志集中收集,快速定位故障
五、故障应对策略
- 连接中断恢复:Angular Socket客户端开启自动重连,重连成功后自动加入原文档房间,请求后端同步最新文档状态
- 实例故障转移:Nginx负载均衡自动将用户请求转发至健康实例,Redis Adapter保证连接状态不丢失,用户无感知切换
- 数据一致性保障:采用乐观锁机制(文档版本号),用户提交编辑时校验版本,冲突时提示用户手动合并;消息队列保证持久化操作的顺序性,避免数据错乱
- 缓存故障处理:Redis宕机时,临时将实时状态降级存储至内存(单实例),待Redis恢复后同步数据;核心文档数据直接从主数据库读取
- 服务降级:系统负载过高时,限制新用户加入,或关闭非核心功能(如历史版本查看),优先保障实时编辑、光标同步等核心功能
内容的提问来源于stack exchange,提问作者Drenky
相关产品推荐
相关产品推荐

