使用event-source实现服务端到客户端的单向推送

1,094 阅读1分钟

最近的开发任务有一项是需要从服务端推送消息到浏览器端,第一时间想到的是websocket和ajax轮询,但是我只需要从服务端推送到客户端,用websocket感觉有点浪费,ajax轮询又不符合要求;于是查了查文档,才发现有更轻量级的解决方案:EventSource。

顾名思义,EventSource可以翻译为事件源,它本质是一个网络事件接口,会在浏览器和服务器之间建立一个持久化的连接,数据以单向的形式从服务端以text/event-stream 格式发送到客户端。想要深入了解请参照mdn文档

建立EventSource很简单,只需如下几个步骤
  1. 服务端准备接口,接口有如下要求:

    • 接口的响应头中的Content-Type 需要设置为:text/event-stream;
    • 服务端准备一个可写流;
    • 服务端返回这个流
    • 服务端往流中写入数据时需要遵循以下格式:
      event: customEvent\n'+"data:"+data+"" + "\n\n
      

    实现代码如下:

    const { Readable, PassThrough } = require("stream");
    const Koa = require("koa");
    const Router = require("koa-router");
    const { start } = require("repl");
    const { promises } = require("fs")
    
    const app = new Koa();
    
    const router = new Router();
    
    router.get("/se", (ctx) => {
      const ss = new PassThrough(); 
      ctx.set({
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        Connection: "keep-alive",
        "Access-Control-Allow-Origin":"*",
        "Access-Control-Allow-Headers":"Content-Type"
      });
      ctx.body = ss;
      // 使用定时器来模拟任务
      setInterval(()=>{
        const date = { date: `当前时间:北京时间${new Date().toLocaleTimeString()}` };
        // 此处的customEventName可以按照自己的需求定义
        const data = 'event: customEventName\n'+"data:"+JSON.stringify(date)+"" + "\n\n";
          ss.push(data);
      }, 1000)
    
    });
    
    app.use(router.routes());
    app.listen(8080, ()=>{
        console.log('server is running')
    })
    
  2. 前端实例化EventSource对象,并监听同名事件,实现代码如下:

    <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
      </head>
      <body>
          <div id='container'></div>
          <script>
              // 实例化eventSource
              const es = new EventSource('http://127.0.0.1:8080/se');
              
              // 监听error事件,可在此处重新订阅
              es.addEventListener('error', (e)=>{
                  console.error(e);
              })
              
              // 监听事件并处理
              es.addEventListener('customEventName', (e)=>{
                  container.innerHTML = e.data;
              })
          </script>
      </body>
      </html>
    

至此,实现了一个简单的单向消息推送;