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

95 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 为什么要做 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 + routeTo`HTTP 头 `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`