实时通知和动态更新还在用websocket?试试sse

Event Stream 是一种允许服务器推送事件到客户端的技术。在 Web 开发中,最常见的实现方式就是 Server-Sent Events (SSE)。

本文将介绍如何使用 Vue 和 Spring Boot 实现基于 Server-Sent Events (SSE) 的 Event Stream 技术。

1. 什么是 Event Stream 和 SSE?

Event Stream 是一种允许服务器推送事件到客户端的技术。与传统的 AJAX 请求不同,SSE 是服务器主动向客户端发送数据流,客户端可以无需反复请求实时接收更新。

主要特点:

  • 单向通信:服务器向客户端推送数据,但客户端不能通过同一连接发送数据回服务器。
  • 基于 HTTP 协议:SSE 使用标准的 HTTP 协议,使用 text/event-stream 内容类型进行数据传输。
  • 持久连接:客户端与服务器之间保持长期连接,服务器可以实时的将数据推送给客户端。

2. 为什么使用 SSE?

与 WebSocket 和轮询相比,SSE 更简单,性能更高:

  • WebSocket 需要双向通信,适用于复杂的双向实时通信场景。
  • 轮询 需要不断请求服务器,增加了不必要的网络流量和延迟。
  • SSE 是单向的,只适合服务器推送数据到客户端,非常适用于通知、实时更新等场景,且性能和延迟更低。

3. Vue实现Event Stream

为方便使用封装工具函数sse.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// @/src/utils/sse.js
/**
* 封装 SSE 连接
* @param {string} url - SSE 的 API URL
* @param {Object} options - 连接选项,支持设置事件监听的回调
* @param {Function} options.onMessage - 收到数据时的回调
* @param {Function} options.onError - 连接错误时的回调
* @param {Function} options.onOpen - 连接建立成功时的回调
* @returns {EventSource} - 返回 EventSource 实例
*/
export function createSseConnection(url, { onMessage, onError, onOpen }) {
// 创建 EventSource 实例
const eventSource = new EventSource(url);

// 连接成功时的回调
if (onOpen) {
eventSource.onopen = () => {
console.log('SSE connection established');
onOpen();
};
}

// 处理服务器推送的数据
if (onMessage) {
eventSource.onmessage = (event) => {
onMessage(event.data);
};
}

// 连接错误时的回调
if (onError) {
eventSource.onerror = (error) => {
console.error('SSE connection error', error);
onError(error);
eventSource.close(); // 连接断开后可以选择关闭连接
};
}

// 返回 EventSource 实例
return eventSource;
}

/**
* 停止 SSE 连接
* @param {EventSource} eventSource - 要关闭的 EventSource 实例
*/
export function closeSseConnection(eventSource) {
if (eventSource) {
eventSource.close();
console.log('SSE connection closed');
}
}

在Vue组件中使用该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div>
<h1>实时数据推送</h1>
<div v-if="message">
<p>服务器推送的数据:{{ message }}</p>
</div>
<button @click="closeConnection">关闭连接</button>
</div>
</template>

<script>
import { createSseConnection, closeSseConnection } from '@/utils/sse';

export default {
data() {
return {
message: null,
eventSource: null,
};
},
created() {
// 使用封装的工具函数建立 SSE 连接
this.eventSource = createSseConnection('http://localhost:8080/api/events', {
onMessage: (data) => {
this.message = data;
},
onError: (error) => {
alert('连接错误');
console.error(error);
},
onOpen: () => {
console.log('SSE 连接成功');
}
});
},
methods: {
// 关闭 SSE 连接
closeConnection() {
closeSseConnection(this.eventSource);
this.message = '连接已关闭';
}
},
beforeDestroy() {
// 在组件销毁时关闭 SSE 连接,避免内存泄漏
if (this.eventSource) {
closeSseConnection(this.eventSource);
}
}
};
</script>

4. Spring Boot 实现 Event Stream

使用 Spring Boot 提供的 SseEmitter 来实现 SSE。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;

@RestController
public class EventStreamController {

@GetMapping("/api/events")
public SseEmitter streamEvents() {
SseEmitter emitter = new SseEmitter();

// 模拟推送实时数据
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send("实时数据:" + i);
Thread.sleep(1000); // 每隔1秒发送一次
}
emitter.complete(); // 完成推送
} catch (IOException | InterruptedException e) {
emitter.completeWithError(e); // 错误时关闭连接
}
}).start();

return emitter;
}
}

6. 注意

  • 连接数限制: 每个客户端都会占用一个HTTP长链接,需要注意服务器并发能力。
  • 数据格式和传输内容: SSE 默认采用文本格式传输数据,数据内容尽量简洁,避免发送过多的二进制数据或复杂的结构。

7. SSE 与 WebSocket 的对比

虽然 SSE 是一个很好的单向推送解决方案,但它与 WebSocket 在某些场景下有所区别:
SSE 适用于单向通信,简单实现,适合推送实时更新数据(如新闻更新、社交网络动态)。
WebSocket 适用于双向实时通信,支持更复杂的互动场景(如聊天室、多人在线游戏、即时消息传递等)。


实时通知和动态更新还在用websocket?试试sse
http://example.com/2023/10/09/sse/
作者
Donghao Ji
发布于
2023年10月9日
许可协议