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來監聽觸發推送。