引言
本文以即时通讯技术的发展历程介绍相关技术以及实现原理,深入探讨其中Server-Sent Events技术的原理、应用场景以及实现方法
即时通讯技术发展历程
HTML标准
- 短轮询:客户端定时向服务器发送HTTP请求,无论有无最新数据都会立即返回响应,这种方式存在很多无效请求,实时性差,服务器负载高
- 长轮询:客户端向服务器发送HTTP请求,服务器会保持这个连接为打开状态,直到有新数据可以发送或连接超时才会返回响应并关闭连接,客户端立即再次发送新的HTTP请求,这种方式需要服务器支持长连接和超时处理,消耗更多资源
- Comet:客户端向服务器发送HTTP请求,服务器会保持这个连接为打开状态,直到有新数据可以发送或连接超时才会返回响应,不会关闭连接,这种方式需要保持大量连接并等待数据更新,消耗更多资源,连接不稳定会导致数据传输终端中断
HTML5标准
- WebSocket:在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,进行双向数据传输
- Server-Sent Events:基于HTTP协议的轻量级推送技术,在客户端与服务器之间建立一条持久化连接,进行单向数据传输,服务器主动向客户端推送事件流数据,定义了一种新的MIME类型text/event-stream,用于标识服务器发送的事件流数据
应用场景
当我们需要双向数据传输时只能选择WebSocket,如果是服务器向客户端单向数据传输时,WebSocket和Server-Sent Events都能实现,优先Server-Sent Events
工作原理
- 建立连接:客户端通过创建一个EventSource对象并指定URL来向服务器发送SSE请求,这个请求时一个普通的HTTP GET请求,服务器会保持连接为打开状态
- 服务器响应:服务器收到SSE请求后,标识出这是一个SSE连接,并设置响应头中的Content-Type为text/event-stream
- 事件发送:服务器通过已建立的HTTP长连接向客户端发送事件,服务器可以连续发送多个事件,客户端会按顺序接收并处理这些事件
- 客户端接收:客户端的EventSource对象监听open、message和error等事件,当连接建立时,会触发open事件,当接收到新的事件时,会触发message事件,并将事件数据作为参数传递给事件处理函数,如果连接断开或出现错误,会触发error事件
示例代码
工程结构
后端
/**
* Server-Sent Events 控制层
* @author muze
*/
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseController {
private final SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE);
/**
* 建立事件流
* produces 指定响应的内容类型为 SSE 所需的文本事件流格式
* @param eventStreamName 事件流名称
* @return SseEmitter实例
*/
@GetMapping(value = "/createEventStream/{eventStreamName}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseEntity<SseEmitter> dataPush(@PathVariable("eventStreamName") String eventStreamName) {
try {
sseEmitter.send(SseEmitter.event().name(eventStreamName).data("建立事件流 [" + eventStreamName + "] 成功"));
} catch (IOException e) {
log.error("建立事件流 [{}] 失败", eventStreamName);
}
return ResponseEntity.ok().body(sseEmitter);
}
/**
* 发送消息
* @param messageDTO 消息DTO对象
*/
@PostMapping("/sendMessage")
public void sendMessage(@RequestBody MessageDTO messageDTO) {
try {
sseEmitter.send(SseEmitter.event().name(messageDTO.getEventStreamName()).data(messageDTO.getMessage()));
} catch (IOException e) {
log.error("消息发送失败:{}", e.getMessage());
}
}
}
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE Demo</title>
</head>
<body>
<h1>Server-Sent Events Demo</h1>
<div id="demo"></div>
<script>
// 检查浏览器是否支持 EventSource 接口
if (!!window.EventSource) {
// 创建一个 EventSource 对象,该对象连接到服务器的 /sse/demo 端点以接收事件
const source = new EventSource('/sse/createEventStream/demo');
// 给名为 demo 的事件添加事件监听器
source.addEventListener('demo', function (response) {
document.getElementById('demo').innerHTML += '<p>' + response.data + '</p>';
});
// EventSource 对象异常时,打印错误信息并关闭EventSource 对象
source.onerror = function (response) {
console.error('EventSource failed:', response);
source.close();
};
} else {
document.getElementById('demo').innerHTML = '<p>Your browser does not support Server-Sent Events.</p>';
}
</script>
</body>
</html>
运行效果
启动项目访问:http://localhost:8080/index.html
使用接口测试工具访问:http://localhost:8080/sse/sendMessage
![](https://i-blog.csdnimg.cn/direct/883d777af62c4ad79e7ad6c8233ab652.png)
至此一个简单的使用Server-Sent Events实现服务器向客户端单向传输数据的示例代码就完成了,在此基础上你可以根据自己的需求扩展工程,可能业务上还会用到策略模式,此时你还可以看看我的另一篇文章Java设计模式系列 - 策略模式:灵活选择算法的行为艺术-CSDN博客,赶快去实际操作一下Server-Sent Events吧,后续我还会写一篇有关WebSocket的文章,希望这篇文章对你有所帮助!