Files
MonocoEditor-With-Lsp-Backend/backend/docs/why-redis-sticky-routing.md
meowrain 3284ce07c7 feat: enhance API and session management with Nacos and Redis integration
- Add Nacos registry for service registration and deregistration.
- Implement Redis registry for session management with heartbeat and session claiming.
- Improve completion service with session handling and request validation.
- Enhance WebSocket handling for completion requests with JSON-RPC support.
- Add tests for new registry implementations and completion manager functionalities.
- Refactor existing code for better readability and maintainability.
2026-02-15 17:46:34 +08:00

3.1 KiB
Raw Blame History

为什么要做 Redis 会话外置 + 粘性路由

背景

当前 LSP 网关的核心能力是:

  • 把编辑器请求转成 LSPJSON-RPC over stdio
  • 为每个 language + sessionId 维护一个长生命周期会话(对应语言服务器进程与文档状态)

单实例时,这套方案天然稳定。
一旦进入微服务部署(多副本 + 负载均衡),如果没有额外机制,会出现严重一致性问题。

不做会发生什么

1. 会话状态丢失

同一个用户会话的请求可能先到实例 A、再到实例 B。
但 B 没有 A 的内存态didOpen/didChange 版本、LSP 上下文),会导致:

  • 补全质量波动
  • 诊断/跳转不一致
  • 重复初始化语言服务器

2. 成本放大

会话不稳定会触发频繁建进程,带来:

  • 更高 CPU/内存
  • 更高请求尾延迟P95/P99
  • 更多瞬时失败

3. 故障不可控

无统一会话归属时,问题难定位(到底是哪台实例持有上下文、何时丢失)。

为什么要 Redis 会话外置

Redis 不是替代 LSP 进程而是做“会话目录Session Directory

  • 记录某个 language + sessionId 当前归属哪个实例
  • 记录实例心跳与可回源地址
  • 给会话绑定 TTL支持自动过期与回收

它带来的价值:

  • 多副本下会话归属可见、可控
  • 实例重启/扩容/缩容时路由行为可预测
  • 可以做统一治理(观测、告警、自动修复)

为什么要粘性路由

LSP 天然是“有状态协议”:同一会话必须持续命中同一实例才能复用上下文。
粘性路由的目标就是保证这一点。

网关当前策略:

  1. 请求到达后先在 Redis claim 会话归属
  2. 如果归属自己:本地处理
  3. 如果归属其他实例:返回 409 + routeToHTTP 头 X-LSP-Route-To
  4. 上游(前端或 Java 网关)据此重试到目标实例

这比纯随机 LB 更符合 LSP 工作方式。

设计取舍

收益

  • 会话一致性显著提升
  • 进程复用率提高,资源更稳
  • 故障域清晰,便于排障

代价

  • 引入 Redis 依赖(网络与可用性要保障)
  • 路由逻辑更复杂冲突重试、TTL 管理)
  • 需要对上游调用方约束:必须稳定传 sessionId

什么时候可以不做

可以暂不启用 Redis/粘性路由的场景:

  • 仅单实例部署
  • 开发/演示环境
  • 对补全一致性不敏感

但只要进入生产多副本,建议启用。

适配 Java 微服务体系的意义

将 LSP 网关作为独立微服务后:

  • Java 业务服务无需关心各语言 LSP 细节
  • 可以统一接入鉴权、限流、链路追踪
  • LSP 能力扩展Go/JS/TS/Java/Python不会反复侵入业务服务

这就是把它做成独立 LSP 服务的核心原因:把状态复杂性收敛在一个可治理的边界里

当前实现对应点

  • Redis 默认配置:10.0.0.10:6379, DB=1, 无密码
  • 会话注册与认领:internal/cluster/redis_registry.go
  • 会话管理与归属检查:internal/completion/manager.go
  • 路由冲突返回:internal/api/handler.go, internal/api/ws_handler.go
  • 启动配置入口:cmd/server/main.go