赞
踩
说起服务端主动推送,大家第一个想到的一定是WEBSOCKET 。
作为软件工程师,不能无脑使用一种技术,要结合实际情况,择优选取。
SSE(Server-Sent Events)相比于WEBSOCKET
1、轻量化、兼容性 基于传统的HTTP协议,所以浏览器兼容性比较好
2、 只支持单向通讯。(服务器->客户端)
- @RestController
- public class SseController {
-
- private final ConcurrentHashMap<String, SseEmitter> emitters = new ConcurrentHashMap<>();
-
- @GetMapping("/subscribe/{id}")
- @CrossOrigin(origins = "*")
- public SseEmitter subscribe(@PathVariable String id) {
- SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
- emitters.put(id, emitter);
- emitter.onCompletion(() -> emitters.remove(id));
- emitter.onError(e -> emitters.remove(id));
- System.out.println(emitter);
- return emitter;
- }
-
- @GetMapping("/unbind/{id}")
- public R deleteItem(@PathVariable String id) {
- this.emitters.remove(id);
- return R.ok(true);
- }
-
- @GetMapping("test/send")
- public void test(){
- this.broadcastMessage("hello world");
- }
-
-
- @Async
- public void broadcastMessage(String message) {
- List<String> keysToDelete = new ArrayList<>();
-
- emitters.forEach((k, v) -> {
- try {
- v.send(message);
- } catch (Throwable t) {
- keysToDelete.add(k);
- }
- });
- keysToDelete.forEach(emitters::remove);
- }
- }
这边是群发消息,也可以根据特定id,发送消息,发送消息可采用异步方式。
订阅消息接口需 支持跨域
设置SSE过期时间为Long的max_value
当建立连接后, 访问test/send 发送hello world
- <template>
- <div id="app">
- <div>
- {{chartStr}}
- </div>
- <div v-for="(item,index) in sseMessage" :key="index">
- {{item}}
- </div>
- </div>
- </template>
- <script>
- export default {
- name: 'App',
- data() {
- return {
- sseMessage: [],
- chartStr: '',
- eventSource: undefined,
- }
- },
- mounted() {
- this.getChartStr()
- this.initSSE()
- },
- beforeUnload() {
- this.unbindSSE();
- },
- methods: {
- /**
- * 初始化服务器发送事件(SSE)连接
- *
- * 此方法创建一个新的 EventSource 对象,连接到后端服务器的指定 URL,
- * 并添加一个 message 事件监听器,用于接收服务器发送的消息。
- */
- initSSE() {
- // 创建一个SSE对象,连接到后端接口
- this.eventSource = new EventSource("http://192.168.0.198:8089/subscribe/" + this.chartStr);
- // 监听message事件,接收后端发送的消息
- // this.eventSource.addEventListener("message", (event) => {
- // //将返回data插入元素
- // this.sseMessage.push(event.data)
- // });
- this.eventSource.onmessage = (event) => {
- this.sseMessage.push(event.data)
- };
- this.eventSource.onerror = function (event) {
- if (event.target.readyState === EventSource.CLOSED) {
- console.log('Connection closed');
- } else {
- console.error('Error occurred', event);
- }
- }
- },
-
- /**
- * 取消订阅 SSE 事件源并解绑图表
- *
- * 这个方法首先检查是否存在有效的 SSE 对象。如果存在,它将关闭这个 SSE 连接。
- */
- unbindSSE() {
- if (this.eventSource) {
- this.eventSource.close();
- }
- fetch('http://192.168.0.198:8089/unbind/' + this.chartStr, {
- method: 'GET'
- })
- .catch(error => {
- console.error('Error unsubscribing:', error);
- });
- },
-
- /**
- * 获取唯一的图表字符串
- *
- * 此方法用于获取一个随机生成的、唯一的图表字符串。
- *
- */
- getChartStr() {
- let array = new Uint32Array(1);
- crypto.getRandomValues(array);
- let randomHex = array[0].toString(16);
- let paddedHex = randomHex.padStart(8, '0'); // 确保32位长度
- this.chartStr = paddedHex
- },
- },
- }
- </script>
- <style>
- #app {
- font-family: Avenir, Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- text-align: center;
- color: #2c3e50;
- }
-
- nav {
- padding: 30px;
- }
-
- nav a {
- font-weight: bold;
- color: #2c3e50;
- }
-
- nav a.router-link-exact-active {
- color: #42b983;
- }
- </style>
效果如下
EventSource
接口提供了一个简单的 API 来接收服务器发送的事件。一旦创建了 EventSource
实例并指向一个 URL,浏览器就会尝试与服务器建立一个持久连接。
text/event-stream
是一种 MIME 类型,用于描述 Server-Sent Events 的数据格式。服务器使用这种格式向客户端发送事件。每个事件由以下几部分组成:
data:
: 必须包含的数据字段,后面跟着实际的事件数据。event:
: 可选的事件类型,如果指定了这个字段,onmessage
事件处理器将收到一个 event
属性,该属性包含了这个事件类型的值。id:
: 可选的字段,用来标识事件的序列号,客户端可以使用它来检测丢失的事件。retry:
: 可选的字段,表示在连接中断后重连前的延迟时间(以毫秒为单位)。通俗解释一下
EventSource
就像是你坐在那里,服务员(服务器)主动把咖啡(信息)端到你的桌子上(浏览器)。你不需要起身询问。
text/event-stream 其实就相当于服务员的托盘,怎么个方式给你把咖啡(信息)送过来。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。