优化实战 第 18 期 - 轻量级的数据实时更新(SSE)

1,096 阅读1分钟

服务器发送事件(Server-Sent Events,简称 SSE) 其实就是浏览器向服务器发送一个 HTTP 请求,然后服务器不断单向地向浏览器推送消息

所谓的消息,其实就是一定格式的文本事件流(数据流)

SSE的特点

  • 基于 http 协议的单向通信

  • 默认支持断线重连,并支持发送自定义事件类型消息

  • 只支持发送文本

  • WebSocket 的一种轻型替代方案,受同源策略的限制

消息的构成

  • 消息唯一标识(id)

    相当于每一条数据的编号,可通过 lastEventId 属性读取。一旦连接中断,浏览器会发送包含 Last-Event-ID: id 头信息来帮助服务器重建连接

  • 消息内容(data)

    只能为文本字符串,发送 JSON 数据 data:${JSON.stringify({ name: 'adiu', alias: '老夫子' })}\n\n

  • 自定义事件类型(event)

    服务器端可自定义,浏览器端通过 addEventListener 监听,如果未指定,则触发浏览器端的 message 事件

  • 最大间隔时间/毫秒(retry)

    如果未指定通信的最大间隔时间,服务器端 3 秒内没有发送任何信息,浏览器端默认开始重连

    由于网络错误导致连接出错,浏览器也会自动重新发起连接

客户端代码

  • 检测浏览器是否支持

    if (!!window.EventSource) {
      console.log('当前浏览器支持事件推送')
    }
    
  • 建立连接

    if (!!window.EventSource) {
      const ets = new EventSource('http://127.0.0.1/sse/notice')
    }
    
  • 连接状态(ets.readyState)

    0 表示连接还未建立,或者连接断线

    1 表示连接已经建立,可以接受数据

    2 表示连接已断,且不会重连

  • 建立连接,触发事件

    ets.addEventListener('open', event => {
      console.log('handle open event')
    })
    

    连接一旦建立,就会触发 open 事件

  • 错误处理

    ets.addEventListener('error', event => {
      console.log('handle error event')
    })
    

    如果发生通信错误(比如连接中断),就会触发 error 事件

  • 自定义事件

    ets.addEventListener('notice', event => {
      const { data, origin, lastEventId } = event
      console.log('消息内容', data)
      console.log('服务器端URL的域名部分,即协议、域名和端口', origin)
      console.log('数据的编号,由服务器端发送。如果没有编号,这个属性为空', lastEventId)
    })
    
  • 关闭推送

    ets.close()
    

    默认情况下,如果客户端和服务端之间的连接关闭,则会自动重连。可以通过 close() 方法终止连接

服务端代码

  • 代码示例(nodejs)

    router
      .prefix('/sse')
      .get('/notice', ctx => {
        ctx.set({
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          'Connection': 'keep-alive'
        })
        ctx.body = `event: notice\nretry: 5000\ndata: ${JSON.stringify({ name: 'adiu', alias: '老夫子' })}\n\n`
      })
    
  • 无推送数据时的空闲时间处理

    : This is a idle stream\n\n
    

    以冒号开头的行,表示注释

    通常,服务器每隔一段时间就会向浏览器发送一个注释,来保持连接不中断

注意事项

  • 由于传输的数据格式必须是文本,所以每个字段之间要用换行符 \n 隔开,最后一个字段要用 \n\n 表示一条消息的结束

  • 服务端发送数据时必须设置响应头标识推送的数据是流信息 Content-Type: text/event-stream

  • 不建议缓存事件推送数据

  • 如果用户量很多,就需要保持很多长连接,因此会占用服务器大量内存和连接数

    一起学习,加群交流看 沸点