sse 通過 deno 的正確打開方式

服務器推送消息的幾種方式

通常情況下,在 bs 架構(gòu)的體系中,服務器想要向客戶端推送消息,主要有以下幾種方式:

  • 短輪詢:客戶端定時向服務器請求數(shù)據(jù),服務器立即返回響應信息。
  • 長輪詢:客戶端向服務器請求數(shù)據(jù),服務器保持連接,直到有新消息才返回響應信息并關閉連接。
  • 流信息:客戶端向服務器請求數(shù)據(jù),服務器聲明要發(fā)送一個數(shù)據(jù)流,連續(xù)不斷地發(fā)送數(shù)據(jù)給客戶端。
  • WebSocket:客戶端和服務器建立一個雙向通信的連接,可以互相發(fā)送和接收數(shù)據(jù)。

這幾種推送消息的方式,實現(xiàn)原理的區(qū)別如下:

  • 短輪詢:不需要特殊的技術支持,只需要客戶端和服務器之間能夠進行 HTTP 請求和響應即可。
  • 長輪詢:不需要特殊的技術支持,只需要客戶端和服務器能夠進行 HTTP 請求和響應即可,但是服務器要能夠保持連接并及時返回新消息。
  • 流信息:需要客戶端支持 EventSource 接口,需要服務器能夠發(fā)送 Content-Type: text/event-stream 的響應,并且遵循一定的格式規(guī)范。
  • WebSocket:需要客戶端支持 WebSocket 接口,需要服務器能夠升級 HTTP 連接為 WebSocket 連接,并且實現(xiàn) WebSocket 協(xié)議。

那么這幾種方式,各自的優(yōu)缺點如下:

  • 短輪詢:優(yōu)點是實現(xiàn)簡單,不需要特殊的技術支持;缺點是效率低,浪費資源,不能實時推送消息。
  • 長輪詢:優(yōu)點是能夠?qū)崟r推送消息,減少無效請求;缺點是服務器壓力大,需要保持連接并及時返回新消息。
  • 流信息:優(yōu)點是能夠?qū)崟r推送消息,節(jié)省資源,避免輪詢的缺陷;缺點是只能單向通信,需要客戶端支持 EventSource 接口。
  • WebSocket:優(yōu)點是能夠?qū)崿F(xiàn)雙向通信,效率高,節(jié)省資源;缺點是需要客戶端和服務器都支持 WebSocket 接口和協(xié)議。

這些方式,各自適合以下使用場景:

  • 短輪詢:適合數(shù)據(jù)更新頻率不高,實時性要求不高的場景,例如新聞、博客等。
  • 長輪詢:適合數(shù)據(jù)更新頻率較高,實時性要求較高的場景,例如聊天、通知等。
  • 流信息:適合數(shù)據(jù)更新頻率較高,實時性要求較高的場景,且只需要單向通信的場景,例如股票行情、在線直播等。
  • WebSocket:適合數(shù)據(jù)更新頻率較高,實時性要求較高的場景,且需要雙向通信的場景,例如在線游戲、視頻會議等。

用 deno 實現(xiàn)一個 Server-sent events 接口

了解完以上這些前置信息后,回到我們這篇文章想要談論的正題上來。

我們知道,Server-sent events 是一種通過 HTTP 實現(xiàn) web 前端應用的服務器端推送的規(guī)范,它是屬于上面我們說到的 流信息 的一種。

以前沒有接觸過的童鞋,有興趣可以閱讀參考下相關的資料:

那么我們?nèi)绾斡?deno,實現(xiàn)一個 Server-sent events 接口呢?

可能有些不了解的童鞋要問了,什么是 deno?

不了解的童鞋可以去 deno 官網(wǎng)了解下,這里不做詳細的介紹。

Deno — A modern runtime for JavaScript and TypeScript

實現(xiàn) http server

首先,我們得了解下,在 deno 中,如何用標準庫實現(xiàn)一個 http server。

最簡單的實現(xiàn)方式,如下面的示例所示:

// 從標準庫中導入 serve 實例
import { serve } from "https://deno.land/std@0.175.0/http/server.ts";

// 請求 http 請求的處理器
function handler(_req: Request): Response {
  return new Response("Hello, World!");
}

console.log("Listening on http://localhost:8000");
// 啟動服務
serve(handler);

實現(xiàn)流模式的響應

deno 官方的示例中,有一個示例展示如何發(fā)送流數(shù)據(jù)。

HTTP Server: Streaming - Deno by Example

我們結(jié)合 Server-sent events 的要求,

我們可以改造一下該示例,改造后的代碼如下所示:

import { Server } from "https://deno.land/std@0.175.0/http/server.ts";

const encoder = new TextEncoder();

// 請求 http 請求的處理器
function handler(_req: Request): Response {

  let timer: number | undefined = undefined;
  const body = new ReadableStream({
    start(controller) {
      // 增加定時器,定時朝流中推送文本數(shù)據(jù)
      timer = setInterval(() => {
        // 文本數(shù)據(jù),遵循一定的格式標準
        const message = `event: message\ndata: It is ${new Date().toISOString()}\n\n`;
        controller.enqueue(encoder.encode(message));
      }, 1000);
    },
    cancel() {
      if (timer !== undefined) {
        clearInterval(timer);
      }
    },
  });

  return new Response(body, {
    // 設置專用響應頭,使 Server-sent events 數(shù)據(jù)能夠正常的傳輸
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache, no-transform",
      "Connection": "keep-alive",
      "X-Accel-Buffering": "no",
      "Transfer-Encoding": "chunked",
    },
  });
}

const server = new Server({ port: 8082, handler: handler });
await server.listenAndServe();

在我們的前端頁面里,可以直接通過下面的方式,調(diào)用該接口:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>sse</title>
  </head>
  <body>
    <div>test</div>
    <script>
      const source = new EventSource("http://host:port", { withCredentials: false });

      source.onopen = function () {
        console.log("EventSource 連接成功");
      };

      source.onmessage = function (event) {
        try {
          if (event.data && typeof event.data === "string") {
            console.log(event.data)
          }
        } catch (error) {
          console.log("EventSource 結(jié)束消息異常", error);
        }
      };

      // 監(jiān)聽錯誤
      source.onerror = function (event) {
        // handle error
        console.log(event);
      };
    </script>
  </body>
</html>

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容