Skip to main content

场景: SSE 断线重连

流式对话过程中网络断开后的重连策略与实现。

场景描述

SSE 流式对话容易受网络波动、代理超时等因素影响断开。客户端需要实现优雅的重连机制,避免用户体验中断。

断线重连时序

重连策略

策略 A: 带 Last-Event-ID 续传

如果服务端支持 Last-Event-ID,断线后可从断点续传:

let lastEventId = null;

async function startStream(query) {
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
};
if (lastEventId) {
headers['Last-Event-ID'] = lastEventId;
}

const response = await fetch(`${BASE_URL}/api/v1/chat/completions`, {
method: 'POST',
headers,
body: JSON.stringify({
messages: [{ role: 'user', content: query }],
stream: true,
}),
});

const reader = response.body.getReader();
// 解析 SSE,更新 lastEventId
}

策略 B: 重新发送请求

服务端不支持断点续传时,重新发起请求并合并结果:

async function streamWithRetry(query, maxRetries = 3) {
let accumulated = '';
let retries = 0;

while (retries < maxRetries) {
try {
const stream = await startStream(query);
for await (const chunk of stream) {
accumulated += chunk;
onChunk(chunk);
}
return accumulated;
} catch (error) {
retries++;
const delay = Math.min(1000 * 2 ** retries + Math.random() * 1000, 30000);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('Max retries exceeded');
}

退避参数

参数建议值
首次重连延迟1s
退避倍数2x
最大延迟30s
最大重试次数3 次
抖动0-1s 随机
避免重连风暴

大量客户端同时断线(如代理重启)时,不加抖动的固定延迟会导致重连风暴。务必加入随机抖动。

预期结果

场景预期行为
短暂断线自动重连,用户几乎无感知
持续断线退避重试后展示错误提示
代理超时检测到连接关闭后重连

排障

问题可能原因
重连后收到重复内容服务端不支持 Last-Event-ID
频繁断线代理 proxy_read_timeout 过短
重连失败 401Token 在断线期间过期

相关链接