SSE介绍
SSE(Server-Sent Events)是一种允许服务器主动向浏览器或其他客户端发送更新的技术。这种通信模式是单工的,即从服务器到客户端(浏览器),而不是双向的。通常用于实时通信场景,如推送通知、实时消息更新等,它是建立在标准的HTTP协议之上,利用现有的Web基础设施(如HTTP缓存、代理和安全机制),易于实现和部署。
使用场景
视频流信息展示及更新:通过SSE由服务器单向发送流媒体信息数据到客户端,创建连接后,服务器开启此连接的定时任务轮询拉取信息发送到客户端,实现实时更新功能。
问题现象
主要涉及服务端资源:
-
SseEmitter连接资源。 -
ScheduledFuture计划任务资源:每3秒请求一次流媒体服务器。 -
客户端使用Https协议与服务端建立SSE连接后,服务器不向客户端发送消息直到达到超时时间断开连接。
获取无人机视频流信息:
服务端与客户端之间创建连接之后,轮询调用流媒体服务器API拉取信息,当无人机视频流停止推流后,流媒体服务器响应码成功但是不包含媒体信息。
在关闭客户端(浏览器)后,服务端没有及时清理连接资源和计划任务,还在不停每隔3秒请求服务器,导致积累的未销毁资源不断增加。
问题排查
复现场景后,跟踪日志发现在一个较长时间之后打印出了资源销毁的记录,可以确定资源最终会销毁,只是没有及时销毁。
查询资料,SSE存在此特性:自带自动重连机制,如果连接断开,浏览器会尝试重新连接。
得出结论:
- TCP连接终止的延迟:当浏览器关闭时,TCP连接可能不会立即终止。操作系统可能会保持连接一段时间,希望能够重用它。这可能导致服务器端在一段时间内无法检测到连接已经断开。
- 在服务器端实现中主要依赖于向客户端发送数据时出现的异常来检测连接断开。如果在发送数据之前没有进行连接检查,那么服务器可能要等到下一次尝试发送数据时才能发现连接已断开。
- 服务器需要正确配置以支持SSE,并且需要在HTTPS连接上保持长连接。如果服务器配置不当,可能会导致连接中断或数据传输问题。
解决方案
-
调整代码逻辑:停止推流后,没有返回数据不跳过发送消息,向客户端发送空对象,如果连接断开后,服务器可以感知到连接断开并销毁资源。
-
设置超时时间:设置连接超时时间1分钟,1分钟内没有向客户端发送任何事件,Spring将调用onTimeout回调,回调中实现资源销毁。
-
nginx添加https支持SSE长连接的配置。