如何将请求路由到特定Heroku Dyno?实时协作应用扩缩容咨询
解决方案与替代架构
一、针对Heroku路由的适配方案
1. 基于Redis的实例位置映射+内部转发
在Redis中维护文档ID与Dyno标识的映射表:
- 当某个Dyno创建
DocumentInstance时,将文档ID -> 当前Dyno名称(可通过环境变量DYNO获取)存入Redis; - 请求到达任意Dyno时,先查询Redis找到对应文档的目标Dyno:
- 若当前Dyno即为目标,直接处理请求;
- 若不是,通过Heroku内部域名(格式
<dyno-name>.<app-name>.herokuapp.com)转发请求到目标Dyno;
- 需处理Dyno重启/销毁场景:Dyno下线时清理Redis中对应映射,后续请求触发重新创建
DocumentInstance并更新映射。
2. 启用Heroku会话粘性(Session Affinity)
Heroku的会话粘性功能可让同一客户端的所有请求路由到同一个Dyno。如果你的协作场景中,单个客户端通常仅操作一个文档,这个方案能快速缓解问题,但存在局限:
- 若客户端同时操作多个文档,不同文档的实例可能分散在不同Dyno;
- 绑定的Dyno挂掉后,客户端请求会被路由到新Dyno,需重新加载文档实例,可能丢失未同步操作。
二、替代架构方案
1. 改用Redis作为集中式OT的权威节点
放弃在Dyno内存中存储DocumentInstance,将文档状态和OT操作日志持久化到Redis:
- 每个Dyno处理请求时,先从Redis获取最新文档状态与操作日志;
- 应用OT转换逻辑生成新状态后写回Redis;
- 利用Redis事务和锁机制,保证多Dyno并发操作的原子性。
这个方案彻底摆脱单Dyno限制,天然支持水平扩展,但需优化Redis读写性能(如使用管道、分片)。
2. 切换为CRDT协作模式
若业务允许,将集中式OT替换为CRDT(无冲突复制数据类型):
- CRDT无需中心权威节点,每个Dyno可独立处理操作,最终自动合并状态;
- 完全适配Heroku水平扩缩容,无需关心请求路由问题;
- 缺点是需修改协作逻辑,Prosemirror生态有相关CRDT插件支持。
3. 文档分片路由
搭建专门的路由Dyno,根据文档ID哈希值将请求分配到对应Dyno组:
- 将文档ID哈希后取模Dyno数量,得到目标Dyno索引;
- 路由Dyno将请求转发到对应Dyno;
- 该方式保证同一文档的请求始终落到同一组Dyno,支持水平扩展,但需额外维护路由节点。
内容的提问来源于stack exchange,提问作者nehalem
相关产品推荐
相关产品推荐

