使用 Server-Sent Events 解决非即时聊天消息的延迟问题

背景

在前端开发中,我们经常会遇到实时消息传输的问题。特别是在聊天室或者实时数据监控等场景下,消息实时性是非常重要的。通常来说,我们可以通过 WebSocket 或者轮询等方式来实现消息传输。但如果是非即时聊天,实时性并不是特别重要,却需要减轻服务器压力,该怎么办呢?这时候 Server-Sent Events 就是一个不错的选择。

Server-Sent Events

Server-Sent Events 是一个 HTML5 的特性,它允许我们从服务器获取推送事件。相比 WebSocket,它更简单,并且可以通过 User-Agent 的缓存机制来降低服务器压力。

Server-Sent Events 的特点如下:

  • 服务器可以不断发送数据给客户端,客户端可以实时接收。
  • 支持断线重连。
  • 支持 HTTP 缓存机制。
  • 传输的数据格式为文本格式。

Server-Sent Events 的数据传输方式类似于 HTTP,但协议规范不同。客户端通过创建一个 EventSource 对象来连接服务器,然后通过监听 message 事件来实时接收数据。

实现

要使用 Server-Sent Events,我们需要做以下几个步骤:

1. 创建一个 EventSource 对象

var eventSource = new EventSource('/api/messages');

通过上述代码,我们创建了一个 EventSource 对象,并指定了服务器的消息推送接口。

2. 监听服务器发送的数据

eventSource.onmessage = function(event) {
  console.log(event.data);
};

通过上述代码,我们可以在控制台打印出来服务器发送的数据。

3. 发送数据

res.write('data: ' + JSON.stringify({ name: 'Tom', message: 'Hello!' }) + '\n\n');

通过上述代码,我们可以在服务器端发送数据给客户端。

4. 关闭连接

eventSource.close();

通过上述代码,我们可以关闭 EventSource 连接。

案例

我们来看一个使用 Server-Sent Events 实现非即时聊天消息传输的案例。

客户端

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>聊天室</title>
  </head>
  <body>
    <ul id="messages"></ul>
    <form>
      <input id="name" placeholder="Name">
      <input id="message" placeholder="Message">
      <button>Send</button>
    </form>
    <script>
      var eventSource = new EventSource('/api/messages');
      eventSource.onmessage = function(event) {
        var data = JSON.parse(event.data);
        var messages = document.getElementById('messages');
        var item = document.createElement('li');
        item.textContent = data.name + ': ' + data.message;
        messages.appendChild(item);
      };
      document.forms[0].onsubmit = function() {
        var name = document.getElementById('name').value;
        var message = document.getElementById('message').value;
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/api/messages');
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(JSON.stringify({ name: name, message: message }));
        return false;
      };
    </script>
  </body>
</html>

服务器端

const http = require('http');
const fs = require('fs');

http.createServer(function(req, res) {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end(fs.readFileSync(__dirname + '/index.html'));
  } else if (req.url === '/api/messages') {
    if (req.method === 'GET') {
      res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
      });
      setInterval(function() {
        res.write('data: ' + JSON.stringify({ name: 'Tom', message: 'Hello!' }) + '\n\n');
      }, 5000);
    } else if (req.method === 'POST') {
      let body = '';
      req.on('data', chunk => {
        body += chunk.toString();
      });
      req.on('end', () => {
        res.end('ok');
        console.log(body);
      });
    }
  }
}).listen(3000);

通过上述代码,我们创建了一个简单的聊天室,服务器每 5 秒向客户端发送一条消息,同时客户端可以通过页面向服务器发送消息,从而实现非即时聊天消息传输。

总结

通过本文,我们了解了如何使用 Server-Sent Events 解决非即时聊天消息的延迟问题。相比 WebSocket,Server-Sent Events 更简单,并且可以通过 User-Agent 的缓存机制来降低服务器压力,对处理大量非即时消息数据流的场景非常适用。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b05a55add4f0e0ff9b5c70