SSE是介于websocket、长短轮训以外的一种服务端推送的方式,用数据流的形式发送文本数据,可想象成网络视频的文字版。他的好处有javascript
详细对比,这里我选择尝试将一个本来基于轮询的web app转到sse上来。虽然这套技术看上去使用很简单,但可能因为普及程度不高和资料较少的缘由,在开发过程当中会遇到不少的坑和要面临的新东西。这里帮你们总结一下,后端使用了koa.js(express应该会更简单)。html
对于一个SSE相应咱们须要返回以下一些HTTP头java
Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
Connection: keep-alive
X-Accel-Buffering: no
复制代码
在其余的教程中提供的http头可能没有这里的全,区别主要在于:node
当设置好header后,咱们就能够写入数据了。通常来讲咱们只须要监听数据的更新而后使用res.write
便可写入数据:react
const onEvent = function(data) {
res.write(`event: message\n`);
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
emitter.on('message', onEvent);
复制代码
咱们用\n
来分隔每一行数据,用\n\n
来分隔每个事件。每个事件中包含事件的type和事件的data,分别用两行来描述。好比上面是返回来一个message事件(若不指定事件类型,则默认message)。下图中咱们还返回来一个withdraw事件,对应的数据行应该是event: withdraw
。nginx
对于koa状况比较复杂,官方不推荐咱们直接操做res对象,而是给context(ctx)对象的body赋值。官方例子git
其实咱们只须要给ctx.body赋一个可写流,关于node流的概念能够看taobao的这篇文章。如官方示例的:github
/** * Create a transform stream that converts a stream * to valid `data: <value>\n\n' events for SSE. */
var Transform = require('stream').Transform;
var inherits = require('util').inherits;
module.exports = SSE;
inherits(SSE, Transform);
function SSE(options) {
if (!(this instanceof SSE)) return new SSE(options);
options = options || {};
Transform.call(this, options);
}
SSE.prototype._transform = function(data, enc, cb) {
this.push(data.toString('utf8'));
cb();
};
复制代码
注意官方实例中有个坑就是默认给每行数据前面加上了data:
前缀,这里删除了。在使用const body = ctx.body = SSE()
后就能够对body对象使用body.write
了。详见官方实例,实例db.js文件中的可读流是可选项。web
客户端(浏览器)的使用就很是简单了。大部分的浏览器支持SSE,并且咱们有针对老浏览器的兼容方案,如Yaffle 。express
使用上真的是特别的简单,并且几乎没有什么坑
const evtSource = new EventSource('/events');
evtSource.addEventListener('event', function(evt) {
const data = JSON.parse(evt.data);
// Use data here
}, false);
复制代码
上面的event
能够替换为你的其余自定义事件。注意这里的链接中断后会自动重连,也许你须要监听onerror事件来作一些额外的处理(API)。致使中断的缘由可能有时间间隔到期、网络错误等。你能够经过定时向客户端返回内容来避免间隔到期:
// Heartbeat
const nln = function() {
res.write('\n');
};
const hbt = setInterval(nln, 15000);
// Clear heartbeat and listener
req.on('close', function() {
clearInterval(hbt);
emitter.removeListener('event', onEvent);
});
复制代码
将轮询替换为sse后仍是很清爽的。注意和websocket不一样sse是单向数据流,咱们在发送消息的时候须要使用其它的接口,能够经过node的events来监听触发推送。