初识SSE
Event Stream | WebSocket |
---|---|
基于HTTP协议 | 独立协议 |
轻量 简单 | 较为复杂 |
默认支持断线重连 | 自己实现 |
传输文本 | 传输二进制 |
HTTP请求、响应头
Content-Type: text/event-stream
数据格式
[field]:value\n
每行数据的格式都是[field]:value\n
,数据以\n\n
结束。
其中field可选项为: 空、id、event、data、retry。
Field可选项介绍
Field为空
:xxx\n
这时客户端要忽略消息内容,可以看作一种心跳机制。
Field为data
data:this is data\n\n
data:data1\n
data:data2\n\n
data表示为数据,一段消息内可以出现多个data。
Field为id
id:messagexxx\n
data:messageyyy\n\n
id为数据的唯一编号,浏览器会保存在lastEventId
属性中,重连的时候会通过Last-event-ID
请求头告知服务器消费位置。
Field为event
event:event1
data:dataxxx\n\n
data:dataxxx\n
data:datayy\n\n
event对应事件名称,可以根据事件名称来订阅事件。
event对应的值为空时,默认为message
。
Field为retry
retry:1000\n
data:xxxxxxx\n\n
指定浏览器重新发起链接的时间间隔,即断开连接后,等待该时间后再重连。
总结
除data外,所有Field都是可选的,data可以出现多次,其余的最多出现一次。
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Demo</title>
</head>
<body>
<h1>SSE Demo</h1>
<div id="messages"></div>
<script>
if (!!window.EventSource) {
const source = new EventSource("/sse");
source.onmessage = function(event) {
const message = document.getElementById('messages');
message.innerHTML += event.data + "<br>";
};
source.onerror = function(error) {
console.log('EventSource failed:', error);
};
source.addEventListener("custom", event => {
const message = document.getElementById('messages');
message.innerHTML += event.data + "<br>";
});
} else {
document.getElementById('messages').innerHTML = "Your browser doesn't support SSE!";
}
</script>
</body>
</html>
@RestController
public class SseController {
private ExecutorService executor = Executors.newSingleThreadExecutor();
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter stream(@RequestHeader(name = "last-event-id", required = false) String lastEventId) {
System.out.println(lastEventId);
SseEmitter emitter = new SseEmitter();
final int continueI = lastEventId == null ? 0 : Integer.parseInt(
lastEventId.substring(lastEventId.lastIndexOf(":") + 2)
);
executor.submit(() -> {
try {
for (int i = continueI; true; i++) {
SseEmitter.SseEventBuilder eb = null;
if (i % 3 == 0) {
eb = SseEmitter.event()
.id(i+"")
.data("SSE message " + i)
.reconnectTime(10000)
.name("message");
} else {
eb = SseEmitter.event()
.id(i+"")
.data("custom message " + i)
.reconnectTime(10000)
.name("custom");
}
emitter.send(eb);
Thread.sleep(3000);
}
} catch (Exception e) {
System.out.println(e.getMessage());
emitter.completeWithError(e);
}
});
return emitter;
}
}