实时更新:轮询、SSE 和 Web Sockets

在本文中,我们将介绍三种技巧/技术来实现实时更新功能:

  1. 轮询+长轮询
  2. SSE(服务器发送事件)
  3. Web Sockets

轮询

这是构建实时应用程序时最简单的方法。

在轮询中,客户端反复向服务器发出请求,希望获得更新/新的数据。不需要额外的步骤来实现这一点。只需将您的 API 调用包装起来即可setInterval。通俗地说,这就像每次几秒钟后刷新你的网页。

也许您会收到更新的数据,也可能不会。没有办法事先知道这一点。

javascript 复制代码
const URL = 'https://jsonplaceholder.typicode.com/posts/';

const fetchPosts = async () => {
  try {
    console.log('Fetching new data...');

    const response = await (await fetch(URL)).json();

    console.log('Data fetched!')

  } catch(err) {
    console.log('Request failed: ', err.message);
  }
}

setInterval(fetchPosts, 5000);

长轮询

既然我们正在讨论这个主题,那么值得在这里讨论一下长轮询。长轮询是轮询的天才/优化版本。

服务器不会立即发送响应,而是等待,直到为客户端提供一些新数据。客户渴望得到回应;这实际上很好,因为客户端没有被阻止并继续执行其他任务。据了解,这也需要在服务器端做出一些努力。

一旦客户端收到数据,它必须为下一个数据状态创建另一个请求。

javascript 复制代码
const URL = "https://jsonplaceholder.typicode.com/posts";

const fetchPosts = async () => {
  try {
    console.log("Fetching new data...");

    const response = await (await fetch(URL)).json();

    console.log("Data fetched!");

    return response;
  } catch (err) {
    console.log("Request failed: ", err.message);
  }
};

const longPoll = async () => {
  // response might be delayed as server might not have updated data
  const response = await fetchPosts();

  if (response) {
    return longPoll();
  }

}

longPoll();

注意: 这些片段提供了最低限度的内容,只是为了传达这个想法。您可能想为此添加更多功能,例如尝试计数或延迟。在代码中添加一些检查也是很好的,这样您就不会最终对自己的服务器进行 DOS 操作。

SSE 服务器发送事件

这是本文中我最喜欢的部分。在此之前,我只了解网络套接字并使用它们,即使对于小型应用程序也是如此。SSE 功能强大、简单,并且可以用最少的代码完成工作。

在SSE中,客户端向服务器发出初始请求以建立连接。发布服务器将更新的数据推送到客户端(只要有可用)。客户无需进一步参与。当然,客户端需要处理这些事件,但仅此而已。

javascript 复制代码
// server-side code in express

app.get("/real-time-updates", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");

  const sendRealTimeUpdates = () => {
    res.write("data: New data!\n\n");
    setTimeout(sendRealTimeUpdates, 3000);
  };

  sendRealTimeUpdates();
});

这是可能的最短实现。

  1. 我们创建了一条GET路线/real-time-updates
  2. Content-Type标题设置为text/event-stream.
  3. 用于res.write()向客户端发送数据。如果我们使用res.send()orres.end()它将关闭连接。

👉重要注意事项

  1. 该消息应始终以 开头data:
  2. 该消息应始终以 结尾\n\n

我们通过res.writesetTimeout.

ini 复制代码
// client-side code in vanilla JS

const URL = 'http://127.0.0.1:3000/real-time-updates';

const sseClient = new EventSource(URL);

sseClient.onopen = () => console.log('Connection opened!');

sseClient.onmessage = (event) => console.log(event.data);

sseClient.onerror = () => console.log('Something went wrong!');

我们使用EventSource接口与 SSE 端点建立连接。

  1. 使用 获取客户端实例EventSource。传递您要订阅的 URL。

  2. 我们得到 3 个事件处理程序,它们被称为不同的阶段。

    • onopen连接打开时调用。
    • onerror发生错误时被调用。
    • onmessage当我们从服务器接收到事件并且我们没有显式处理该事件时被调用。
  3. 我们还获得了一种close可用于随时关闭连接的方法。

如果我们不在服务器上指定事件类型,默认情况下,每个事件都具有类型message。因此,处理程序onmessage捕获每个事件。

但是,如果我们使用关键字指定事件,event:我们可以在客户端显式处理它。

swift 复制代码
// diff: server-side code with custom event

res.write("event: notification\ndata: New data!\n\n");
javascript 复制代码
// diff: client-side code with custom event handling

sseClient.addEventListener('notification', (event) => {
    console.log(event.data))
};

这就是添加您自己的 SSE 所需的全部代码。

⚠️当SSE通过HTTP/1.1实现时,它会受到最大连接数的限制;即 6。这意味着任何网站www.fake-dev.to最多可以在浏览器中打开 6 个 SSE 连接(包括多个选项卡)。建议使用 HTTP/2,默认限制为 100,但可以配置。

Web Sockets

Web Sockets 比上述方法更强大,但也带来了额外的复杂性。

Web Sockets 形成双工连接,这意味着客户端和服务器都可以在单个通道上相互发送数据,而 SSE 是单向的。

Web Sockets 由 HTTP 握手请求发起,但后来升级到 TCP 层。

HTTP 协议是无状态协议,这意味着所有标头(包括 cookie、令牌等)都会随每个请求一起发送。这使得它可以水平扩展。如果服务器 1 过载,请求可以由服务器 2 处理,并且由于我们在标头中拥有所有信息,因此没有什么区别。这也使得速度变慢,因为每个请求都需要发送更多数据。此外,一旦请求得到满足,连接就会关闭。因此,对于新请求,必须再次打开连接,这非常耗时。

另一方面,TCP 是有状态的。Web 套接字速度更快,因为连接保持活动状态以进行通信,并且每个请求都不会发送额外的标头。但这也使得扩展变得更加困难。如果客户端正在与服务器 1 通信,则所有请求应仅由服务器 1 处理。其他服务器不知道它的状态。

相关推荐
superconvert15 小时前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
喵不拉几2 天前
WebSocket 协议
网络·websocket·网络协议
Flying_Fish_roe2 天前
Spring Boot-WebSocket相关问题
spring boot·后端·websocket
下一秒_待续3 天前
C# 使用Socket通信,新建WinForm服务端、客户端程序
websocket·c#·socket·winforms
一只小白菜~3 天前
实现实时Web应用,使用AJAX轮询、WebSocket、还是SSE呢??
前端·javascript·websocket·sse·ajax轮询
黑金IT4 天前
WebSocket vs. Server-Sent Events:选择最适合你的实时数据流技术
网络·python·websocket·网络协议·fastapi
Jiaberrr4 天前
如何在微信小程序中实现WebSocket连接
前端·javascript·websocket·微信小程序·小程序
bigcarp5 天前
linux + 宝塔 + django + websocket 部署
linux·websocket·django
weixin_473894776 天前
对WebSocket的理解
网络·websocket·网络协议
乐辞6 天前
WebSocket和HTTP协议有什么区别
websocket·网络协议·http