核心机制概述
在 SSE 协议中,客户端需要处理两种重要的重连场景:
- 心跳重连:主动检测连接是否存活
- 超时重连:响应网络或服务器超时
以下是详细实现机制和决策流程图:
mermaid.png
graph TD
A[建立SSE连接] --> B{连接状态}
B -->|活跃| C[接收数据]
B -->|空闲| D[启动心跳检测]
C --> E{数据流中断?}
E -->|是| F[触发超时重连]
E -->|否| B
D --> G{收到心跳?}
G -->|是| B
G -->|否| H[心跳超时]
H --> F
F --> I[计算重连延迟]
I --> J[延迟重连]
J --> K[尝试重连]
K --> L{重连成功?}
L -->|是| B
L -->|否| M[指数退避]
M --> I
一、心跳机制实现
目的
防止中间设备(代理、防火墙)因空闲而关闭连接
实现方案
服务器端心跳:
// Node.js 示例
setInterval(() => {
res.write(': heartbeat\n\n'); // 发送注释行作为心跳
}, 15000); // 每15秒发送一次
客户端心跳检测:
// LDSwiftEventSource 实现
class EventSource {
private var heartbeatTimer: Timer?
private let heartbeatInterval: TimeInterval = 20
func startHeartbeatMonitor() {
heartbeatTimer = Timer.scheduledTimer(withTimeInterval: heartbeatInterval,
repeats: true) { [weak self] _ in
if self?.lastMessageDate.timeIntervalSinceNow ?? 0 < -self.heartbeatInterval {
self?.handleHeartbeatTimeout()
}
}
}
private func handleHeartbeatTimeout() {
closeConnection(reason: .heartbeatTimeout)
scheduleReconnect()
}
}
心跳决策表
条件 | 动作 | 重连延迟 |
---|---|---|
15秒内收到数据 | 重置计时器 | - |
15-20秒无数据 | 警告日志 | - |
>20秒无数据 | 断开重连 | 1秒 |
>30秒无数据 | 视为连接死亡 | 3秒 |
二、超时重连机制
超时类型及处理
-
连接超时(TCP层)
- 原因:DNS失败、服务器宕机
- 处理:立即重试(1秒延迟)
-
读取超时(应用层)
- 原因:服务器停止发送
- 处理:指数退避重连
-
写入超时
- 原因:网络拥塞
- 处理:线性增加重试间隔
指数退避算法实现
class EventSource {
private var reconnectAttempts = 0
private let initialReconnectDelay: TimeInterval = 1.0
private let maxReconnectDelay: TimeInterval = 60.0
func scheduleReconnect() {
reconnectAttempts += 1
// 计算退避延迟
let delay = min(
initialReconnectDelay * pow(2.0, Double(reconnectAttempts)),
maxReconnectDelay
)
DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
self.connect()
}
}
func resetReconnectState() {
if Date().timeIntervalSince(lastConnectedDate) > backoffResetThreshold {
reconnectAttempts = 0
}
}
}
超时决策矩阵
错误类型 | 状态码 | 是否重连 | 重连延迟策略 |
---|---|---|---|
DNS解析失败 | - | 是 | 线性增加 |
TCP连接超时 | - | 是 | 指数退避 |
TLS握手失败 | - | 是 | 固定间隔 |
HTTP 401 | 401 | 否 | - |
HTTP 403 | 403 | 否 | - |
HTTP 404 | 404 | 否 | - |
HTTP 429 | 429 | 是 | 使用Retry-After头 |
HTTP 500 | 500 | 是 | 指数退避 |
HTTP 502/503/504 | 502+ | 是 | 指数退避 |
三、连接断开决策点
应主动断开连接的情况
-
客户端主动关闭:
eventSource.close()
-
服务器指示关闭:
- HTTP 204 No Content
- HTTP 401 Unauthorized
-
致命协议错误:
case invalidContentType case invalidUTF8Stream case frameSizeExceeded
-
资源管理需求:
- 应用进入后台
- 电池电量不足
应保持连接的情况
-
短暂网络波动:
- 自动重连机制处理
-
服务器维护:
- HTTP 503 + Retry-After
-
客户端休眠唤醒:
// AppDelegate.swift func applicationDidBecomeActive(_ application: UIApplication) { eventSource.reconnect() }
四、完整重连流程图
mermaid.png
sequenceDiagram
participant Client
participant Network
participant Server
Client->>Server: 发起SSE连接
Server-->>Client: 200 OK / text/event-stream
loop 活动连接
Server->>Client: 定期发送数据/心跳
Client->>Client: 重置超时计时器
end
alt 心跳超时
Client->>Client: 检测到20秒无数据
Client->>Server: 关闭连接
Client->>Client: 调度1秒后重连
else 读取超时
Network--xClient: 网络中断
Client->>Client: 检测到30秒无数据
Client->>Server: 关闭连接
Client->>Client: 调度指数退避重连
end
Client->>Server: 重连请求
alt 重连成功
Server-->>Client: 200 OK
Client->>Client: 重置重连计数器
else 重连失败
Server-->>Client: 503 Service Unavailable
Client->>Client: 增加重连计数器
Client->>Client: 计算新延迟时间
Client->>Server: 延迟后再次重连
end
五、最佳实践建议
客户端配置参数
参数 | 推荐值 | 说明 |
---|---|---|
初始重连延迟 | 1000ms | 首次重连等待时间 |
最大重连延迟 | 30000ms | 最大等待时间 |
退避重置阈值 | 60000ms | 连接稳定多久重置计数器 |
心跳检测间隔 | 15000ms | 检查连接活跃频率 |
读取超时阈值 | 30000ms | 无数据多久视为超时 |
服务器端实现建议
-
心跳机制:
setInterval(() => { res.write(`: ${Date.now()}\n\n`); }, 15000);
-
重连指导:
// 429 Too Many Requests res.setHeader('Retry-After', '10'); // 10秒后重试
-
连接关闭协议:
// 正常关闭 res.write('event: end\ndata: goodbye\n\n'); res.end();
异常处理增强
func onError(error: Error) {
switch error {
case let sseError as SSEError where sseError.isFatal:
closePermanently()
case let urlError as URLError where urlError.code == .timedOut:
scheduleReconnect(delay: 3.0)
case let httpError as HTTPError where httpError.statusCode == 429:
if let retryAfter = httpError.headers["Retry-After"] {
scheduleReconnect(delay: Double(retryAfter))
} else {
scheduleReconnect()
}
default:
scheduleReconnect()
}
}
六、性能优化策略
-
自适应心跳:
func adjustHeartbeatInterval() { let networkQuality = estimateNetworkQuality() heartbeatInterval = networkQuality > 0.8 ? 30.0 : 10.0 }
-
优先级队列:
let reconnectQueue = DispatchQueue( label: "com.yourapp.sse.reconnect", qos: .utility )
-
电池感知:
NotificationCenter.default.addObserver( self, selector: #selector(batteryStateChanged), name: UIDevice.batteryStateDidChangeNotification, object: nil ) @objc func batteryStateChanged() { if UIDevice.current.batteryState == .unplugged { maxReconnectDelay = 120.0 // 延长重连间隔省电 } }
总结
在 SSE 实现中,健壮的重连机制需要:
-
双重检测:
- 主动心跳检测预防中间设备断开
- 被动超时检测响应网络故障
-
智能退避:
- 指数退避避免服务器过载
- 重置机制恢复连接效率
-
精确断连:
- 区分可恢复和不可恢复错误
- 遵循服务器指示关闭连接
-
上下文感知:
- 根据网络质量调整参数
- 考虑设备状态优化资源使用
通过实现这些机制,SSE 客户端可以保持高效稳定的实时连接,同时优雅地处理各种网络异常场景。