SSE(Server-send event) 服务器发送事件
本文首发于个人博客 Cyy’s Blog
转载请注明出处 https://cyyjs.top/blog/5b835dbd30e02e324caa737b
最近开发项目时,需要实现一个通知功能;当时想到的技术方案有两种:
- HTTP Polling 轮询方式
- 使用 WebSocket
第一种方式比较原始,有很多缺点,浪费带宽资源,也不实时;WebSocket虽然好,但对于目前业务来说,有点重,因为项目其实只需要从服务器发通知到客户端,不需要双向交互。后来发现了一项新的技术SSE
# 什么是 SSE
SSE即Server-send event,是服务器推送事件。一个网页获取新的数据通常需要发送一个请求到服务器,也就是向服务器请求的页面。使用server-sent event的方法,服务器可以在任何时刻向我们的web页面推送数据和信息。
# 如何使用
# 客户端
1、创建EventSource对象,传入一个服务器接口地址
const eventSource = new EventSource('http://127.0.0.1:3000/sse')
2、监听服务器发送的消息
eventSource.onmessage = function(e) {
console.log(`message-data: ${e.data}`);
}
// 或
eventSource.addEventListener('message', e => {
console.log(`message-data: ${e.data}`);
}, false);
// 监听自定义事件
eventSource.addEventListener('event-1', e => {
console.log(`message-data: ${e.data}`);
}, false);
// 监听错误
eventSource.onerror = function (error) {
console.log(error)
}
# 服务端
服务器端发送的响应内容应该使用值为"text/event-stream"的MIME类型,并按照固定的事件流格式即可。
这里以Nodejs作为服务器为例:
const http = require('http');
const fs = require('fs');
http.createServer(function (req, res) {
if (req.url === '/sse') {
res.writeHead(200, {
"Content-Type": "text/event-stream" //设置头信息
});
let data = {
id: 1,
message: 'hello'
}
// 发送消息
res.write(
'event: event-1' + data.id + '\n' +
'data:' + JSON.stringify(data) + '\n\n', // 必须“\n\n”结尾
);
} else {
// 返回页面
fs.readFile('./sse.html', function (err, content) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
});
}
}).listen(3000);
这样客户端就可以接收到服务器发送的数据了。
# 如何指定用户发送
如果我们到通知是以广播的形式发送,就很简单了,所有客户端监听同样的事件就可以了;那么要对指定用户发送通知,该如何实现呢?
目前我采用的方式是,客户端,根据用户id来监听不同的事件,例如用户id为1就监听event-1,这样服务端发送的时候,就指定发送的事件名称为event-1即可。
# 如何在程序中调用发送通知接口
例子中可以看到,当使用http请求http://127.0.0.1:3000/sse接口时,就会触发服务器发送事件,但是我们实际应用中场景中,触发条件肯定不会是这样的;比如我修改了数据,或者调用了别的接口,就需要去触发通知,怎么办呢?
这里使用Nodejs的events,修改服务端代码如下:
var http = require('http');
var fs = require('fs');
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
http.createServer(function (req, res) {
if (req.url === '/sse') {
res.writeHead(200, {
"Content-Type": "text/event-stream" //设置头信息
});
myEmitter.on('sseEvent', (data) => { // 监听事件调用
// 发送消息
res.write(
'event: event-1' + data.id + '\n' +
'data:' + JSON.stringify(data) + '\n\n', // 必须“\n\n”结尾
);
});
} else {
// 返回页面
fs.readFile('./sse.html', function (err, content) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
});
}
}).listen(3000);
然后在需要发通知的地方直接触发事件就行了:
myEmitter.emit('sseEvent', data);
# 参考链接
