SSE介绍
- 基于标准的 HTTP:SSE 建立在标准的 HTTP 协议之上,因此不需要额外的插件或协议。客户端通过普通的 HTTP 连接就可以接收来自服务器的推送消息。
- 单向通信: SSE 是一种单向通信协议,只允许服务器向客户端推送数据,而客户端不能向服务器发送数据。这使得它非常适合用于服务器向客户端发送实时更新或事件通知。
- 文本格式: SSE 使用文本格式进行数据传输,因此易于理解和调试。传输的数据以事件流的形式发送,每条消息由一个或多个字段组成,包括事件标识、数据和可选的注释。
- 自动重连机制: SSE 包含自动重连机制,当连接断开时,客户端会自动尝试重新连接服务器,从而保持通信的持续性。
SSE是一个轻量级协议,相对简单;WebSocket是一种相对较重的协议,相对复杂。但SSE只支持单向交互(服务器给客户发送),Websocket支持双向交互。
数据格式方面, SSE 使用的是 UTF8 编码的文本格式。(这个点很多教程都不说)
SSE的HTTP response 里header Content-Type 的值是 text/event-stream。不允许改变!
结论
在 Web 开发中,SSE 常用于实时更新网页内容、实时通知、实时数据推送等场景。在基于 Java 的 Web 应用中,Spring Framework 提供了对 SSE 的良好支持,可以使用 SseEmitter 或者 WebFlux 中的 Server-Sent Event 类来实现服务器端的 SSE 功能。
SSE的数据格式
每个SSE的消息响应分为4个元素:
- retry:重试时间,单位毫秒,只能为数字(SSE请求失败,就会发送新的请求)
- id:消息ID(自定义)
- event:时间类型(自定义)
- data:消息的内容(自定义)
下图是4个消息,注意,多个消息之间中间会有个空行(\n\n)。单个消息之间元素间隔是换行\n
retry: 5000 id: 1cd7bb64-4341-4f5d-a690-4298b8a8ae20 event: eventType data: Sun Nov 20 18:23:11 CST 2022 retry: 5000 id: 2f57295d-3eaa-4e5c-a787-55fff58d9b05 event: eventType data: Sun Nov 20 18:23:12 CST 2022 retry: 5000 id: 6a9618de-99e7-4c03-91f8-dcdd7601a8d0 event: eventType data: Sun Nov 20 18:23:13 CST 2022 retry: 5000 id: c5fdcc90-b1f7-4058-9a3a-d63881ffea8b event: eventType data: Sun Nov 20 18:23:14 CST 2022
SpringBoot接入SSE
不依赖于任何Jar包(这里前提是你的项目支持MVC哈,99%的项目是不需要的)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version> <!-- 请替换为您需要的版本号 -->
</dependency>
本质只是一个Http协议,然后指定ContentType,然后返回相应的格式!
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE链接测试</title>
</head>
<body>
消息类型是message消息:
<div id="ssediv">默认消息</div>
<br>
消息类型是diyEventType消息:
<div id="diyssediv">DIY SSE消息</div>
</body>
<script>
var sse = new EventSource("http://localhost:8081/sse");
/**
* 默认是没有指定eventTtpe的消息,但eventType就为message。
* 等价于addEventListener("message" ...
*/
sse.onmessage = function (ev) {
console.error("这里只能处理eventType为message的消息")
var elementById = document.getElementById("ssediv");
elementById.innerHTML = ev.data;
}
/**
* 添加指定类型消息处理,eventType是后台自定义的
*/
sse.addEventListener("diyEventType",event => {
console.error("自定义事件"+event.data)
var elementById2 = document.getElementById("diyssediv");
elementById2.innerHTML = event.data;
})
/**
* SSE连接异常
*/
sse.onerror = function (){
alert("服务器已停止!")
}
/**
* SSE连接成功
*/
sse.onopen = function (){
alert("服务器已连接!")
}
// 不要忘记关闭断开连接哦
// sse.close()
</script>
</html>
后端代码
@Controller
public class SSE {
@GetMapping(value = "/sse")
@ResponseBody
public String getMessage(HttpServletResponse response) {
// System.out.println("请求进入了");
response.setContentType("text/event-stream"); // 指定ContentType,不可变
response.setCharacterEncoding("utf-8"); // 指定响应字符集,是否可变,没测试,但建议指定utf-8
while (true) {
String s = "";
s += "retry: 5000\n"; // 客户端没有获取到数据,就不断发送新的请求(间隔5秒),直到有数据。
s += "id: " + UUID.randomUUID() + "\n"; // 这里指定消息ID
s += "event: eventType\n"; // 这里定义事件类型,自定义!
s += "data: " + new Date() + "\n\n"; // 这里设置返回的数据
try {
PrintWriter pw = response.getWriter();
Thread.sleep(1000L);
pw.write(s);
pw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
测试(请关注network样式 ,只有Chrome才能看到,safari、firefox都不支持)
注意避免跨域,浏览器 URL 用 localhost,前端 SSE 的 URl 不能是127.0.0.1。
注意一点:sse的消息实际上只会有一个网络请求。消息藏在EventStream中。每条消息会在此EventStream中是一个记录。下文是客户端接受到服务端多条消息推送的示例。
如果Jmeter直接打到请求上,200个连接,直接导致任何请求无法进行。停止Jmeter后,依旧无法进行请求。lsof -i:服务端口,都是closed也无法
请看第二篇 SSE
更加贴近业务操作的代码
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤