项目需求:数据展示实时更新
解决方案:在第一时间想到的是通过前端轮询的方式请求后端接口,达到数据更新的目的,但是存在以下缺点
- 数据不能做到实施更新,有一定时间的延迟
- 消耗大量的系统资源
优化方案:使用EventSource的方式来实现该需求,能够解决以上问题
目录
1. EventSource基本介绍
EventSource是一种用于实现服务器推送事件的Web API,它允许服务器向客户端发送数据流,而无需客户端发出请求。EventSource架构的设计思想是建立一种持久连接,通过这个连接发送事件流,以实现通信。
2. EventSource特性
- 实时性。EventSource能够实现实时地数据传输,可以在服务器端有新事件时立即向客户端推送,并自动进行展示。
- 低延迟。由于EventSource采用长连接的方式进行传输,相比于普通的轮询方式,它能够更加高效地传输数据。
- 易用性。EventSource是一种非常易用的API,只需要在客户端创建一个EventSource对象,指定服务器端的URL,即可进行监听并展示事件。
- 兼容性。EventSource能够同时兼容WebSocket和长轮询等方式,具备很好的兼容性,可以在各种不同的场景下使用。
3. EventSource工作原理
EventSource的原理基于Http协议,它允许服务器与客户端建立持久的连接,以便服务器能够向客户端推送事件数据。以下是EventSource的工作原理客户端发起请求:客户端通过创建一个EventSource对象,并向服务器发起一个HTTP GET请求,请求一个特定的URL。
-
服务器响应:服务器接收客户端的请求,并在HTTP头中添加
Content-Type: text/event-stream
,表明将发送的事件数据格式为text/event-stream。 -
建立持久连接:服务器与客户端建立一个持续的HTTP连接,并开始向客户端发送数据,直到连接被关闭。
-
发送事件数据:当服务器有新的事件数据要发送时,它将按照特定格式发送给客户端。事件数据通常以
event:eventName;data:eventData;
的形式发送,其中event
字段表示事件名,data
字段表示事件数据。 -
客户端接收数据:客户端通过EventSource对象注册事件处理函数,以接收来自服务器的事件数据。当接收到事件数据时,客户端会创建一个Event对象,并触发相应的事件处理函数,传递Event对象作为参数。Event对象包含
type
(事件类型,通常为"message")、data
(事件数据)、lastEventId
(上一个事件的ID)、origin
(事件源的URL)等属性。 -
错误处理和重连:如果连接出现错误或被关闭,客户端将触发“error”事件或“close”事件,以便进行错误处理或重新连接。
-
单向通信:EventSource支持单向通信,即只能从服务器向客户端推送数据,不支持客户端向服务器发送数据。这种特性使它适用于需要实时更新的应用程序场景,如聊天室、股票市场等。
-
自动重连接:EventSource支持自动重连接功能,如果连接中断,它可以尝试自动恢复连接。
-
发送随机事件:EventSource允许服务器发送没有明确事件名的随机事件,这种情况下,如果没有指定事件名的匹配事件处理函数,则会触发通用事件处理函数。
总的来说,EventSource提供了一种简单的方式来实现服务器向客户端的实时数据推送,特别适合于不需要双向通信的场景。
4. EventSource常用方法
1. 创建EventSource对象,其中url是服务器端推送数据的地址。
var evtSource = new EventSource(url);
2. onopen:连接建立时触发
evtSource.onopen = function () {
console.log("Connection to server opened.");
};
3. onmessage:接收到服务器端发送的数据时触发。
evtSource.onmessage = (e) => {
console.log(e.data)
};
4. onerror:连接出错时触发。
evtSource.onerror = function () {
console.log("连接失败");
};
5. 实战案例:Vue+SpringBoot
前端代码:
methods: {
handleEventSoure() {
const eventSource = new EventSource('/source/test') // 连接地址
eventSource.onopen = () => { // 开始连接
console.log("连接成功")
}
eventSource.onmessage = (event) => { // 接收消息
console.log(event.data)
}
eventSource.onerror = () => { // 连接失败
console.log("连接失败")
}
},
},
mounted() {
this.handleEventSoure()
}
后端代码:
@RestController
@RequestMapping("/source")
public class SysLoginInfoController extends BaseController {
@GetMapping("/test")
public Flux<String> test(HttpServletResponse response, HttpServletRequest req) {
//设置响应的内容类型为HTML,字符集为UTF-8。
response.setContentType("text/html;charset=utf-8");
// 设置响应头,指明这是一个EventSource响应
response.setHeader("Content-Type", "text/event-stream");
//设置响应头"Cache-Control"为"no-cache",表示不允许缓存此响应。
response.setHeader("Cache-Control", "no-cache");
//设置响应头"Connection"为"keep-alive",表示保持连接。
response.setHeader("Connection", "keep-alive");
//返回的是一个字符串流 返回数据:好的 + 当前时间
return Flux.interval(Duration.ofSeconds(1)).map(sequence -> "data: 好的" + new Date() + "");
}
}
效果展示:
至此大功告成
年少鸡鸣方就枕,老人枕上待鸡鸣。转头三十余年梦,不道消磨只数声。