分析您当前的前后端通讯架构,整体是成熟可靠的,但针对生产环境的长期连接稳定性,有几个可以加固的点。
webui/index.html 作为单页应用,与后端 src/web.ts(端口 3010)的交互主要有两类:
| 通讯模式 | 用途 | 技术实现 | 连接特性 |
|---|---|---|---|
| HTTP REST(短连接) | 登录/注册、加载会话列表、获取模型列表、记忆搜索、文件浏览 | fetch() API | 请求-响应,一次性 |
| SSE(长连接) | 流式聊天 | EventSource | 持续单向流(服务器→前端) |
聊天时前端通过 EventSource 连接到后端的 SSE 端点(例如 /api/chat/stream),服务器推送 data: 事件,前端实时渲染。其他操作都是普通的 HTTP 请求。
这种设计本身是标准的前后端分离方案,没有明显架构缺陷。
retry 字段),但遇到网络闪断、后端重启或代理超时后,前端需要确保能重新建立连接并恢复上下文,否则聊天会"卡住"且用户无感知。Access-Control-Allow-Origin),且 EventSource 默认不带 Authorization 头,需通过 URL 参数或 cookie 携带 token,可能存在安全风险。onerror 并重连,REST 请求需有重试逻辑避免操作失败。EventSource 的 onerror 事件,实现自定义退避重连(例如 1s → 2s → 4s → 最大 30s)。Last-Event-ID 头),后端支持从断点续传(需要后端记录每个客户端的进度,较复杂;若不支持断点续传,至少重连后能重新开始流)。后端定期(如每 30 秒)发送 SSE 注释事件(:\n\n 或 event: ping\ndata: {}\n\n),前端忽略它但可维持连接活跃,防止代理静默断开。
对所有 fetch() 请求封装一个简单的重试函数(如自动重试 2 次,指数退避),并统一处理网络错误(用户 Toast 提示"网络异常,请稍后重试")。
如果使用 JWT,可以考虑在登录后由后端设置 HttpOnly cookie(需要同源,若前后端不同域则不可行)。如果必须用 Authorization 头,EventSource 无法自定义头,可采用以下方案:
fetch 手动解析流(response.body.getReader())替代 EventSource,从而完全控制头部和重连。EventSource,将 token 作为查询参数 ?token=... 传递,后端从查询参数提取验证(需确保使用 HTTPS,避免 token 在 URL 中暴露)。使用 Service Worker 或简单的 localStorage 缓存正在输入的消息,待连接恢复后自动重发,防止用户写了一半的消息丢失。
后端监听进程信号(SIGTERM),关闭前向所有 SSE 客户端推送一个 event: shutdown 事件,前端收到后显示"服务器维护中",并进入更快速的重连尝试。
稳定性尚可,但仍需加固。
目前的基础通讯功能(SSE + REST)在本地开发或低负载场景下完全可以正常工作,但若要长期跑在公网上,SSE 无重连、无心跳是最大的痛点。建议优先实施 建议 1 + 2,改动量小、收益高。
是否需要我针对 webui/index.html 和 src/web.ts 给出具体的代码修改补丁,来实现 SSE 自动重连和心跳?这样改动仅涉及前端约 50 行和后端约 15 行,风险极低。