如何在接收SSE事件时立即终止TIdHTTP.Get()请求及相关问题
关于Indy TIdHTTP处理SSE的终止与连接断开问题
一、如何立即终止GET请求
你当前依赖OnChunkReceived事件触发Abort的方式,只能等待下一个chunk到达才能生效,无法实现“立即”终止。更可靠的方案是直接调用TIdHTTP.Disconnect()方法,同时配合线程状态检查,具体实现如下:
1. 主线程终止逻辑
在主线程中发起终止时,先标记线程为终止状态,再主动断开HTTP连接,最后等待线程退出并释放:
// 主线程调用终止 NotificationThread.Terminated := True; if Assigned(NotificationThread.HTTP) then NotificationThread.HTTP.Disconnect; NotificationThread.WaitFor; NotificationThread.Free;
2. 调整线程Execute循环
将原有的repeat...until改为while循环,提前检查终止状态,避免不必要的请求重试:
procedure TAPINotificationThread.Execute; begin HTTP := CreateHTTP; var ms := TMemoryStream.Create; try try while not Terminated do begin try HTTP.Get(URL, ms); except on E: EIdConnClosedGracefully do Log('EIdConnClosedGracefully reconnecting...'); on E: EIdReadTimeout do Log('EIdReadTimeout reconnecting...'); on E: Exception do Log('Exception in Notification Thread - ' + E.ClassName + ': ' + E.Message); end; ms.Clear; // 每次请求后清空流 end; except on E: EAbort do Log('Notification thread terminating normally'); end; finally HTTP.Free; ms.Free; end; end;
注意:绝对不要直接在主线程调用Free()释放TIdHTTP组件,这会导致未完成的网络操作访问已释放内存,引发访问违规或资源泄漏。必须先断开连接,等待线程完全退出后再释放资源。
二、服务器每330秒触发EIdConnClosedGracefully的原因
这个异常是服务器主动优雅断开连接导致的,核心原因是服务器端配置了连接超时限制:
- 多数SSE服务会设置固定的连接时长(330秒=5分30秒是常见配置),防止连接长时间占用服务器资源;
- 也可能是服务器的反向代理(如Nginx、Apache)或负载均衡器配置了
keepalive_timeout、proxy_read_timeout为330秒,超时后主动断开连接; - SSE协议本身就要求客户端在连接断开后自动重连,你当前代码中的重连逻辑完全符合规范,无需修改。
额外优化建议
- 你设置的
ReadTimeout为30分钟,远大于服务器的330秒超时,可调整为略大于服务器超时时间(如360秒),避免不必要的长等待; - 在
OnChunkReceived中检查到Terminated时,可直接调用HTTP.Disconnect()替代Abort,更快触发连接终止; - 确保
TThread.Queue中的逻辑不会阻塞,避免影响线程的终止响应速度。
内容的提问来源于stack exchange,提问作者Alister
相关产品推荐
相关产品推荐

