FastGPT icon indicating copy to clipboard operation
FastGPT copied to clipboard

v4.9.6 SSEServerTransport多mcp_server容器Pod兼容性问题

Open liukewia opened this issue 9 months ago • 4 comments

例行检查

  • [x] 我已确认目前没有类似 issue
  • [x] 我已完整查看过项目 README,以及项目文档
  • [x] 我使用了自己的 key,并确认我的 key 是可正常使用的
  • [x] 我理解并愿意跟进此 issue,协助测试和提供反馈
  • [x] 我理解并认可上述内容,并理解项目维护者精力有限,不遵循规则的 issue 可能会被无视或直接关闭

你的版本

  • [ ] 公有云版本
  • [x] 私有部署版本, 多pod部署mcp_server, 具体版本号: 4.9.6

问题描述

当前使用SSEServerTransport的实现在多mcp_server容器Pod部署时存在会话不连贯问题。由于会话状态存储在Pod内存中(transportMap),当负载均衡器将初始SSE请求和后续POST请求分发到不同Pod时,系统无法找到对应的transport实例,导致请求挂起直到超时。这严重影响了系统在Kubernetes等容器编排环境下的可扩展性和可靠性。

复现步骤

  1. 部署当前代码到两个或多个Pod的Kubernetes集群:
const transportMap: Record<string, SSEServerTransport> = {};

app.get('/:key/sse', async (req, res) => {
  const transport = new SSEServerTransport(`/${key}/messages`, res);
  transportMap[transport.sessionId] = transport;
  // 设置服务器和处理程序...
  await server.connect(transport);
});

app.post('/:key/messages', (req, res) => {
  const { sessionId } = req.query as { sessionId: string };
  const transport = transportMap[sessionId];
  if (transport) {
    transport.handlePostMessage(req, res);
  }
  // 如果transport不存在,请求会挂起
});
  1. 配置负载均衡器(如Nginx或Kubernetes Ingress)分发请求
  2. 客户端(fastgpt容器pod)首先连接到/:key/sse端点获取sessionId
  3. 然后客户端发送POST请求到/:key/messages?sessionId=xxx
  4. 观察当请求随机分发到不同mcp server Pod时出现的连接挂起超时情况

预期结果

  1. 系统应该能够处理多Pod环境中的请求,无论请求分发到哪个Pod
  2. GET和POST请求应该能够跨Pod正常工作
  3. 客户端体验应保持一致,不受底层Pod分配的影响
  4. 即使GET和POST请求被路由到不同的Pod,也不应出现挂起或超时
  5. 系统应该在水平扩展时保持会话一致性,支持无状态水平扩展

实现方案可以采用Redis等共享存储保存会话状态,并通过StreamableHTTPServerTransport的无状态模式或者改进的SSEServerTransport处理机制来解决这个问题。

解决方案

重构以支持分布式部署,可以采用以下方法之一:

方案1:在无状态模式下使用StreamableHTTPServerTransport

  • 使用sessionIdGenerator: undefined初始化
  • 为每个请求创建新的transport/server实例
  • 使用Redis进行跨Pod会话状态存储

方案2:简化SSE + 共享会话存储

  • SSE端点仅返回带sessionId的URL,然后关闭
  • 在Redis中存储会话数据并设置适当的TTL
  • 为每个POST请求创建新的transport/server

实现要求

  1. 在Redis中存储最小化的会话数据,而不是内存中
  2. 为每个请求创建新的transport而不是重用
  3. 对StreamableHTTP使用无状态模式或在发送endpoint后关闭SSE连接
  4. 处理请求前通过分布式存储验证会话
  5. 这种架构可以实现跨多个Pod的水平扩展,同时保持会话一致性。

liukewia avatar Apr 25 '25 05:04 liukewia

sse 默认不支持多副本,改造起来 过于复杂 ,意义 不大 。 httpsreamable 协议大部分 client 还未支持,还需要等待支持,这个比较容易适配多副本。

c121914yu avatar Apr 25 '25 05:04 c121914yu

higress 有对应负载方案,不过不太普适。

c121914yu avatar Apr 25 '25 05:04 c121914yu

感觉等待 HTTP streamable 成熟即可,这东西有状态感觉意义没多大,感觉无状态+单次流连接的模式足够了,还稳定。

c121914yu avatar Apr 25 '25 05:04 c121914yu

sse 默认不支持多副本,改造起来 过于复杂 ,意义 不大 。 httpsreamable 协议大部分 client 还未支持,还需要等待支持,这个比较容易适配多副本。

感觉现在client升级到最新版都支持streamable了,其次 mcp spec 在2025-03-26版本已废弃sse,推荐使用streamable。

建议 mcp server模块可支持streamable,开放非 /sse 的路径,如spec文档例子中的 /mcp ,如果需要兼容旧sse可保留旧代码。这样对于私有部署用户来说是灵活的,可根据自身部署方式和需求按需选择。

liukewia avatar Apr 26 '25 16:04 liukewia

已支持 https://github.com/labring/FastGPT/pull/4695,无需额外部署 mcp_server

c121914yu avatar Apr 28 '25 04:04 c121914yu