服务器往浏览器推消息(SSE)应用
1,SSE 和 WebSocket 对比
SSE(服务器发送事件) SSE是一种基于HTTP的单向通信机制,用于服务器向客户端推送数据。它的工作原理如下:
建立连接:客户端通过发送HTTP请求与服务器建立连接。在请求中,客户端指定了接收事件的终点(Endpoint)。 保持连接:服务器接收到连接请求后,保持连接打开,并定期发送事件数据给客户端。 事件流:服务器使用 “Content-Type: text/event-stream” 头部标识SSE连接,并使用特定格式的数据(事件流)发送给客户端。 客户端处理事件:客户端通过JavaScript的 EventSource 接口监听SSE连接,一旦接收到事件,就可以处理数据并更新页面。 SSE的特点和适用场景:
单向通信:SSE是从服务器到客户端的单向通信模型,只能由服务器推送数据给客户端。 实时更新:SSE适用于需要实时更新数据的应用场景,如股票行情、新闻推送等。 简单易用:使用SSE相对简单,无需额外的库或框架支持,可以直接使用浏览器的原生API进行开发。
WebSocket WebSocket是一种全双工的通信协议,它通过在客户端和服务器之间建立持久连接,实现双向通信。WebSocket的工作原理如下:
握手阶段:客户端向服务器发送WebSocket握手请求,服务器返回握手响应。在这个阶段,客户端和服务器协商选择协议和版本。 建立连接:握手成功后,客户端和服务器之间建立持久连接,可以进行双向数据传输。 双向通信:一旦连接建立,客户端和服务器都可以主动发送消息给对方。数据可以以文本或二进制格式进行传输。 断开连接:当任一方决定关闭连接时,可以发送关闭帧来终止连接。 WebSocket的特点和适用场景:
双向通信:WebSocket支持双向通信,客户端和服务器可以互相发送消息。 实时互动:WebSocket适用于实时互动的应用场景,如聊天应用、协作编辑等。 复杂性和灵活性:相对于SSE,WebSocket更为灵活,可以处理更复杂的通信需求。它允许自定义消息格式、心跳检测、连接状态管理等。
2,具体代码实现
2.1 后端
@GetMapping(value = "/stock",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamStockPrice() {
SseEmitter emitter = new SseEmitter();
// 模拟生成实时股票价格并推送给客户端
Random random = new Random();
new Thread(() -> {
try {
while (true) {
// 生成随机的股票价格
double price = 100 + random.nextDouble() * 10;
// 构造股票价格的消息
String message = String.format("%.2f", price);
// 发送消息给客户端
emitter.send(SseEmitter.event().data(message));
// 休眠1秒钟
Thread.sleep(1000);
}
} catch (Exception e) {
emitter.completeWithError(e);
}
}).start();
return emitter;
}
2.2 前端
$("#btn12").click(function (){
const eventSource = new EventSource('http://localhost:8080/test/test/stock');
eventSource.onmessage = function (event) {
showMsg(event.data)
}
})
2.3 最终效果
SSE 演示
3,传统部署方式
nginx部署
4,k8s部署方式
待研究
5,问题
问题 1:采用 nginx 部署时,页面刷新很慢,实际上服务器已经发了很多数据过来。
此问题待研究解决。
解决方法:
nginx 做相关配置
配置如下
location /codeTest {
proxy_pass http://127.0.0.1:18080/test;
# proxy_http_version设置代理使用的HTTP协议版本为1.1。
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# SSE 连接时的超时时间
proxy_read_timeout 86400s;
# 取消缓冲
proxy_buffering off;
# 关闭代理缓存
proxy_cache off;
# 禁用分块传输编码
#chunked_transfer_encoding off
}
/codeTest是你希望使用的路径,你可以根据需要进行修改。 proxy_pass指定了后端服务器的地址,你需要将其替换为实际的后端服务器地址。 proxy_http_version设置代理使用的HTTP协议版本为1.1。 proxy_set_header设置一些必要的头部信息,如连接方式、真实客户端IP等。 proxy_read_timeout 指令来设置 SSE 连接的超时时间。默认情况下,Nginx 会在 60 秒后关闭空闲的连接,这对于 SSE 来说是不合适的,所以我们将超时时间设置为一天(86400 秒)。这样,客户端和服务器之间的连接可以持续保持打开状态。 proxy_buffering off 指令来确保数据可以实时传输,而不需要等待缓冲区满。在SSE请求中禁用缓冲,以便正确处理SSE流式数据。 proxy_cache 对于 SSE(Server-Sent Events)连接,通常不建议启用 Nginx 的代理缓存(proxy_cache)。因为 SSE 是一种长连接技术,它通过保持持久连接来实时推送数据给客户端,而代理缓存会将响应数据缓存起来并在后续请求中返回缓存的响应,这与 SSE 的工作方式相违背。如果启用代理缓存,Nginx 可能会缓存 SSE 的数据,并在后续的连接中返回相同的缓存数据,这样会导致客户端收到重复的消息,破坏了 SSE 的实时性和准确性。 chunked_transfer_encoding 参数可以根据你的需求决定是否关闭。在 SSE 中,通常不需要禁用分块传输编码,因为它允许将数据以数据块的形式逐步传输,与 SSE 的流式数据特性相符合。 proxy_pass 指令正确反向代理到你的 SSE 应用程序的地址和端口,以使连接正确工作。
问题 2:连接一段时候,自动断开
解决方法参考如下文章
Springboot调整接口响应返回时长(解决响应超时问题)