跳到主要内容
Deno 2.4 发布,带来 deno bundle、字节/文本导入、稳定版 OTel 等功能
了解更多

边缘的 Web 流

在 Deno,我们非常重视 Web 标准。因此,Deno Deploy 对 Web Streams(也称为“标准流”)提供了卓越的支持。通过 Deno Deploy,只需几行 JavaScript(或 TypeScript)代码,即可构建一个流式、事件驱动的服务器,并将其即时部署到全球 28 个地区的数据中心

让我们看看浏览器标准在服务器端取得了多大进展…

基本 HTTP 代理

构建 HTTP 代理时,重要的是不要缓冲正文。那样会增加内存使用量并降低响应时间。相反,您希望通过服务器将 HTTP 消息的正文流式传输回客户端。

这是一个直接的例子:

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

async function handler(req: Request): Promise<Response> {
  const url = new URL(req.url);
  url.protocol = "https:";
  url.hostname = "example.com";
  url.port = "443";
  return await fetch(url.href, {
    headers: req.headers,
    method: req.method,
    body: req.body,
  });
}

serve(handler);

您可以通过 https://example-proxy-requests.deno.dev/ 访问此代理服务器,或在 https://dash.deno.com/playground/example-proxy-requests Fork 代码。

带转换功能的 HTTP 代理

如果我们想修改通过代理的数据怎么办?在下面的示例中,我们借助 TransformStreamTextDecoderStreamTextEncoderStream,逐包处理正文,将文本转换为大写。

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

serve(async (req) => {
  const url = new URL(req.url);
  url.protocol = "https:";
  url.hostname = "example.com";
  url.port = "443";
  const resp = await fetch(url.href);

  const bodyUpperCase = resp.body
    .pipeThrough(new TextDecoderStream())
    .pipeThrough(
      new TransformStream({
        transform: (chunk, controller) => {
          controller.enqueue(chunk.toUpperCase());
        },
      }),
    )
    .pipeThrough(new TextEncoderStream());

  return new Response(bodyUpperCase, {
    status: resp.status,
    headers: resp.headers,
  });
});

您可以通过 https://example-proxy-upper-case.deno.dev/ 访问此服务器,或在 https://dash.deno.com/playground/example-proxy-upper-case Fork 代码。

服务器发送事件

当然,您不需要代理即可使用流。如果想构建一个每秒响应一条消息的服务器怎么办?这可以通过结合使用 ReadableStreamsetInterval 来实现。

此外,通过将 content-type 设置为 text/event-stream 并为每条消息添加 "data: " 前缀,服务器发送事件 可以使用 EventSource API 轻松处理。

您可以通过 https://server-sent-events.deno.dev/ 实时访问,或在 https://dash.deno.com/playground/server-sent-events Fork 代码。

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

const msg = new TextEncoder().encode("data: hello\r\n\r\n");

serve(async (_) => {
  let timerId: number | undefined;
  const body = new ReadableStream({
    start(controller) {
      timerId = setInterval(() => {
        controller.enqueue(msg);
      }, 1000);
    },
    cancel() {
      if (typeof timerId === "number") {
        clearInterval(timerId);
      }
    },
  });
  return new Response(body, {
    headers: {
      "Content-Type": "text/event-stream",
    },
  });
});

请注意,由于 Deno Deploy 使用 HTTP/2,SSE 不会受到浏览器最大开放连接数 (6) 的限制,该限制使得在 HTTP/1.1 上使用 SSE 不明智。

WebSockets

Deno Deploy 还支持 WebSocket 连接。WebSockets 不属于 Stream API 的一部分,但它们的使用场景有很大的重叠。

目前还没有针对服务器端 WebSockets 的标准 API,因此为此您必须使用 Deno 命名空间中的 Deno.upgradeWebSocket

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

serve((req) => {
  const upgrade = req.headers.get("upgrade") || "";
  if (upgrade.toLowerCase() != "websocket") {
    return new Response("request isn't trying to upgrade to websocket.");
  }
  const { socket, response } = Deno.upgradeWebSocket(req);
  socket.onopen = () => console.log("socket opened");
  socket.onmessage = (e) => {
    console.log("socket message:", e.data);
    socket.send(new Date().toString());
  };
  socket.onerror = (e) => console.log("socket errored:", e.message);
  socket.onclose = () => console.log("socket closed");
  return response;
});

您可以通过 https://websocket.deno.dev/ 实时访问,或在 https://dash.deno.com/playground/websocket Fork 代码。

下一步是什么?

查看更多示例画廊文档

Deno Deploy 目前处于 Beta 阶段,对所有人免费开放。如果您试用它,请通过向我们发送反馈来提供帮助。