SSE ( Server-sent Events )是 WebSocket 的一种轻量代替方案,使用 HTTP 协议。
严格地说,HTTP 协议是没有办法做服务器主动推送的,但是当服务器向客户端声明接下来要发送流信息时,客户端就会保持连接打开,SSE 使用的就是这种原理。
一、SSE 能做什么?
理论上, SSE 和 WebSocket 做的是同一件事情。当你需要用新数据局部更新网络应用时,SSE 可以做到不需要用户执行任何操作,便可以完成。
举例我们要做一个统计系统的管理后台,我们想知道统计数据的实时情况。类似这种更新频繁、 低延迟的场景,SSE 可以完全满足。
其他一些应用场景:例如邮箱服务的新邮件提醒,微博的新消息推送、管理后台的一些操作实时同步等,SSE 都是不错的选择。
二、sse与webSocket的区别
- websocket是双向通道的,即服务端可以主动向客户端推送消息,客户端也可以向服务端推送消息。
- Sse是单向通道的, 即服务端可以主动向客户端推送消息,但是客户端向服务端推送消息则需要新建一个http请求 ,这跟websocket做对比无疑是增大了开销。
三、选择sse还是webSocket
当客户端频繁向服务端发信息时,建议使用websocket。反之,两者在其他情况下都差不多。
四、sse的语法
每个事件由类型和数据两部分组成,同时每个事件可以有一个可选的标识符(事件id)。不同事件的内容之间通过仅包含回车符和换行符的空行(“\n\n”)来分隔。每个事件的数据可能由多行组成。
data: a //推送的信息数据
data: b //推送的信息数据
id: 1916 //事件id
event: messsagePush //事件类型
retry: 10000 //连接超时时间
: hhh //:冒号前面的类型为空,则表示这是行是注释不会被解析
data: b //推送的信息数据
id: 1916 //事件id
retry: 10000 //连接超时时间
如上所示,每个事件之间通过空行来分隔。对于每一行来说,冒号(“:”)前面表示的是该行的类型,冒号后面则是对应的值。可能的类型包括:
- 类型为空白,表示该行是注释,会在处理时被忽略。
- 类型为 data,表示该行包含的是数据。以 data 开头的行可以出现多次。所有这些行都是该事件的数据。
- 类型为 event,表示该行用来声明事件的类型。浏览器在收到数据时,会产生对应类型的事件。
- 类型为 id,表示该行用来声明事件的标识符(也就是事件id)。
- 类型为 retry,表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间。
每行结束后都需要换行,不同的事件类型之间需要通过间隔一行来做区分。
如果在服务器端不指定event的事件类型,则客户端使用默认的事件类型message。
如果在服务器端指定event的事件类型(event: 事件类型),则客户端需要指定相同的事件类型的监听器来做监听。
对于服务器端返回的响应,浏览器端需要在 js 中使用 EventSource 对象来对事件进行监听。只需要在对象上添加相应的事件监听处理方法。EventSource 提供了三个标准事件
五、案例演示:
服务端:
- 将响应内容类型content-type设置成text/event-stream。 告诉客户端,服务端要通过流推送消息
- 每行结束都要以 "\n\n" 结尾,否则客户端无法对Sse语法进行解析
- 响应Sse格式的数据给客户端
@RestController
@RequestMapping("/sse")
@CrossOrigin
public class SseController {
/**
* 将响应内容 content-type设置成text/event-stream。 告诉客户端,服务端要通过流推送消息
* @return
*/
@GetMapping(value="/message/push",produces = "text/event-stream;charset=utf-8")
public String sendMessage(){
try {
Thread.sleep(1000); //线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//实时消息推送
StringBuilder builder = new StringBuilder();
builder.append("data: hello world\n\n");//消息数据
builder.append("id: 1916\n\n"); //事件id
/** 消息事件
* 如果不设置event字段,则客户端触发默认事件类型message。
* 如果设置,则客户端触发事件类型为event字段对应设置的值。
*/
builder.append("event: sseMessage\n\n"); //消息事件
builder.append("retry: 10000\n\n"); //设置连接超时时间 毫秒
builder.append("\n\n"); //该事件类型结束,不同数据类型之间用\n\n(隔一行)来区分
return builder.toString();
}
}
客户端:
创建一个EventSource实例来对访问服务接口,并对该接口进行监听。
<script>
/**
* 构造方法中传入服务端的接口
获取客户端连接对象
* @type {EventSource}
*/
var sourse = new EventSource("http://localhost:8080/sse/message/push");
// 连接成功时触发该事件监听器
sourse.onopen=function (event) {
console.log("客户端与服务端连接成功……");
}
// 事件监听器: 服务端响应客户端时(推送消息)时触发事件监听器
// 如果服务端指定 event: 事件类型时; 客户端需要设置该事件类型的监听器。
sourse.onsseMessage=function (event) {
console.log(event);
console.log("推送消的消息"+event.data); //推送的消息
}
// 如果服务端不指定 event: 事件类型时; 客户端则使用默认的事件监听器message
sourse.onmessage=function (event) {
console.log(event);
console.log("推送消的消息"+event.data); //推送的消息
}
// 执行出错时触发该事件监听器
sourse.onerror=function (event) {
// console.log(event);
console.log("执行出错……");
}
</script>