Files
AIclinicalresearch/docs/03-业务模块/SSA-智能统计分析/07-统计专家配置/SAE 生产环境 SSE 故障诊断与终极防御.md
HaHafeng 5c5fec52c1 fix(aia,ssa,asl,infra): harden SSE transport and stabilize attachment context
Deliver SSE protocol hardening for SAE/HTTP2 paths, add graceful shutdown health behavior, and improve SSA retry UX for transient stream failures. For AIA, persist attachment extraction results in database with cache read-through fallback, plus production cache safety guard to prevent memory-cache drift in multi-instance deployments; also restore ASL SR page scrolling behavior.

Made-with: Cursor
2026-03-09 18:45:12 +08:00

128 lines
6.2 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.
# **SAE 生产环境 SSE 协议故障诊断与终极防御指南**
**故障现象:** SAE 部署后,前端偶发 net::ERR\_HTTP2\_PROTOCOL\_ERROR后端日志显示请求已接收甚至已完成。第二次请求恢复正常。
**故障定性:** 云原生环境下的经典长连接断裂与 HTTP/2 协议翻译冲突。
**核心认知:** 在 Serverless 容器中,不要试图“防止连接断开”(做不到),必须通过“前端智能重连”和“后端优雅停机”来容错。
## **一、 为什么你们的修复“治标不治本”?**
你们已经做了非常出色的网络层修复:
1. **去除了 Connection: keep-alive**:防止 HTTP/2 严格模式下因为禁用的连接专有头部Connection-Specific Headers导致强制 RST\_STREAM。
2. **条件化了 Connection: Upgrade**:防止 Nginx 把普通的 SSE 长轮询当成 WebSocket 升级,导致协议错乱。
**为什么还是会偶发失败?**
因为当 SAE 进行滚动更新Rolling Update旧的 Node.js Pod 会收到 SIGTERM 信号准备退出。此时,阿里云 CLB负载均衡的连接池中可能还有几十条保持活跃的 HTTP/2 物理连接。
如果浏览器复用了这条即将被回收的 HTTP/2 通道来发起新的 SSE 请求,或者旧 Pod 直接被底层硬杀Kill \-9Nginx 往后端转发时会遭遇 Connection Refused 或 Broken Pipe。Nginx 无法优雅地把这个错误包装成 HTTP 状态码,只能粗暴地向客户端发送一个 HTTP/2 GOAWAY 帧,浏览器收到后就会报出 ERR\_HTTP2\_PROTOCOL\_ERROR。
## **二、 终极防御三板斧Cloud-Native Resilience**
要 100% 消除用户的报错感知必须在前端、Nginx 和后端落地以下三个机制:
### **🪓 第一斧:前端 SSE 智能断线重连 (Intelligent Retry)**
这是解决问题的绝对核心对于大模型对话Chat和分析执行Execute前端绝不能因为一次底层网络闪断就把红色错误拍在用户脸上。
**改造方案:** 在 useSSAChat.ts 或底层 SSE 请求库中(推荐使用微软的 @microsoft/fetch-event-source 库,它自带强大的重试机制),拦截网络层错误并静默重试。
import { fetchEventSource } from '@microsoft/fetch-event-source';
async function startSseStream(url: string, payload: any) {
let retryCount \= 0;
const MAX\_RETRIES \= 3;
await fetchEventSource(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
// 关键:拦截底层 HTTP/2 协议错误并决定是否重试
async onopen(response) {
if (response.ok) retryCount \= 0; // 连接成功,重置计数器
},
onerror(err) {
retryCount++;
if (retryCount \> MAX\_RETRIES) {
throw err; // 超过重试次数,才真正向用户报错
}
// 记录日志,但不抛出异常
console.warn(\`\[SSE\] 网络闪断,正在进行第 ${retryCount} 次重连...\`, err);
// 返回一个延迟时间 (指数退避) 告诉底层库多久后重连
return Math.min(1000 \* (2 \*\* retryCount), 5000);
},
onmessage(msg) {
// 处理正常的 SSE 事件
}
});
}
*效果:即便 SAE 正在滚动部署CLB 断开了连接,浏览器会在 1 秒内静默发起第二次请求,此时 CLB 已经指向了新 Pod用户毫无感知只会觉得“这次思考慢了一秒”。*
### **🪓 第二斧Nginx 层彻底关闭缓存 (Disable Buffering)**
如果你们的 Ingress 或前置 Nginx 开启了代理缓冲Proxy Buffering它是 SSE 流式输出的绝对天敌,也是加剧协议错误的元凶。
**改造方案:** 必须为 /api/v1/ssa/\*/chat 等 SSE 接口单独关闭 Nginx 缓冲。
location \~ ^/api/v1/ssa/.\*(?:chat|stream)$ {
proxy\_pass http://backend\_upstream;
\# 以下三行是 SSE 救命神药
proxy\_http\_version 1.1;
proxy\_buffering off; \# 严禁 Nginx 缓存 Chunk 数据
proxy\_cache off; \# 严禁缓存
chunked\_transfer\_encoding on;
\# 彻底清理可能导致 H2 冲突的 Headers
proxy\_set\_header Connection '';
\# 延长超时时间(应对大模型深度思考)
proxy\_read\_timeout 120s;
}
### **🪓 第三斧Node.js 后端优雅停机 (Graceful Shutdown)**
为什么部署期间连接断裂那么惨烈?因为 Node.js 默认在收到 SAE 的部署停止信号SIGTERM时会立刻自杀。
**改造方案:** 在 main.ts 或 app.module.ts 中实现 Graceful Shutdown。当收到退出信号时拒绝新的请求但**给已经建立的 SSE 长连接留出 30 秒的执行时间**。
// Node.js Express/NestJS 优雅停机示例
let isShuttingDown \= false;
// 1\. 探针接口:一旦开始停机,立刻告诉 SAE/CLB "我不健康了",不要再给我派发新请求
app.get('/health', (req, res) \=\> {
if (isShuttingDown) {
return res.status(503).send('Shutting down');
}
res.status(200).send('OK');
});
// 2\. 捕获系统终止信号
process.on('SIGTERM', () \=\> {
console.log('\[System\] 收到 SIGTERM 信号,准备优雅停机...');
isShuttingDown \= true;
// 停止接收新连接
server.close(() \=\> {
console.log('\[System\] 所有现有连接已处理完毕,安全退出。');
process.exit(0);
});
// 强制超时机制:如果过了 30 秒还有 SSE 流没跑完(比如陷入死循环),强制退出
setTimeout(() \=\> {
console.error('\[System\] 优雅停机超时 (30s),强制退出。');
process.exit(1);
}, 30000);
});
## **三、 架构师总结**
你们在本地开发环境HTTP/1.1 直连,无负载均衡,无 Pod 销毁)永远无法复现这个问题。
**这不单纯是网络配置问题,这是 Serverless 架构下的“状态保持”冲突。** 请优先让前端团队加上 **@microsoft/fetch-event-source 的静默重连机制**,这不仅能解决 SAE 部署期间的报错还能解决未来医院网络环境不稳定、用户切网WIFI 换 5G导致的所有诡异长连接断开问题。这是投入产出比ROI最高的一记绝杀。