订阅以接收新文章的通知:

通过 TCP and Sockets for Workers 建立连接

2021/11/15

7 分钟阅读时间
Making connections with TCP and Sockets for Workers

今天,我们很高兴地宣布,我们正在开发 API 和基础结构以在 Cloudflare Workers 中支持更多基于 TCP、UDP 和 QUIC 的协议。一旦发布,这些新功能有可能实现使用非 HTTP 套接字与 Worker 或 Durable Object 互相连接,就像我们如今使用 HTTP 和 WebSockets 一样简单。

目前,Cloudflare Workers 支持使用标准化的 fetchWebSocket API 打开 HTTP 和 WebSocket 连接的功能。通过在内部进行一些更改使其可在 Workers 中运作,我们制作了一个示例,使用现有的驱动程序(在本示例中,是基于 Deno 的 Postgres 客户端驱动程序)通过 WebSocket(通过安全的 Cloudflare Tunnel)与远程 Postgres 服务器通信。

import { Client } from './driver/postgres/postgres'

export default {
  async fetch(request: Request, env, ctx: ExecutionContext) {
    try {
      const client = new Client({
        user: 'postgres',
        database: 'postgres',
        hostname: 'https://db.example.com',
        password: '',
        port: 5432,
      })
      await client.connect()
      const result = await client.queryArray('SELECT * FROM users WHERE uuid=1;')
      ctx.waitUntil(client.end())
      return new Response(JSON.stringify(result.rows[0]))
    } catch (e) {
      return new Response((e as Error).message)
    }
  },
}

此示例的工作方式是,使用标准 fetch 和 WebSockets API 替换使用 Deno 特定 TCP 套接字 API 的 Postgres 客户端驱动程序的位数。然后,我们建立了与一个远程 Cloudflare Tunnel 守护程序的 WebSocket 连接(该守护程序在 Postgres 服务器附近运行),从而建立有效的 TCP-over-WebSockets。

Connecting to a backend data center via a Cloudflare Tunnel
Connecting to a backend data center via a Cloudflare Tunnel

尽管在无需对 Cloudflare Workers 运行时进行任何更改的情况下,我们能够构建出该示例并和 Postgres 服务器有效且高效地通信这一事实令人印象深刻,但这一方法存在许多限制。第一点是,该解决方案需要额外的基础结构来建立和维护 WebSocket 通道,在本示例中,是在 Postgres 服务器附近运行的 Cloudflare Tunnel 守护程序的实例。当然,我们很乐意向客户提供该守护程序,但如果根本不需要该组件,那自然更好。第二点是,通过 WebSockets 隧道传输 TCP,其本身是通过 HTTP over TCP 进行隧道传输,这并非最优解。这可以运作,但我们可以做得更好。

从 Cloudflare Workers 进行连接

目前,没有标准 API 用于 JavaScript 中的套接字连接。我们想要改变此状况。

如果您之前使用过 Node.js,那么您极有可能熟悉 net.Socketnet.TLSSocket 对象。如果您使用 Deno,那么您可能知道他们最近引入了 Deno.connect()Deno.connectTLS() API。当您查看这些 API,立即就能发现它们彼此之间的差别有多大,尽管它们都是用来做完全相同的事情。

当我们决定添加从 Workers 中打开和使用套接字连接的功能时,我们也一致认为,我们的确没有兴趣开发另一个特定于平台且与其他平台所提供之 API 不同的非标准 API。因此,我们将邀请扩展到需要套接字功能的所有 JavaScript 运行时平台,以合作开发新的(且最终标准化的)API,无论您选择在哪个运行时上进行开发,该 API 都可以运作。

下面是我们构想用于从单个 TCP 客户端连接打开和读取内容的大致示例:

const socket = new Socket({
  remote: { address: '123.123.123.123', port: 1234 },
})
for await (const chunk of socket.readable)
  console.log(chunk)

或者,此示例使用 UDP 发送一个简单的“hello world”数据包:

const socket = new Socket({
  type: 'udp',
  remote: { address: '123.123.123.123', port: 1234 },
});
const enc = new TextEncoder();
const writer = socket.writable.getWriter();
await writer.write(enc.encode('hello world'));
await writer.close();

该 API 将设计得足够通用,能够与客户端和服务器端合作;可用于 TCP、UDP 和 QUIC;具有或不具有 TLS,且不会依赖于特定于任何单一 JavaScript 运行时的任何机制。它将在现有广泛支持的 Web 平台标准基础上构建,例如 EventTargetReadableStreamWritableStreamAbortSignalpromises。原本熟悉 fetch() API、service workers 和使用 async/await 的 promises 的开发人员都会熟悉该新 API。

interface Socket : EventTarget {
  constructor(object SocketInit);

  Promise<undefined> update(object SocketInit);

  readonly attribute ReadableStream readable;
  readonly attribute WritableStream writable;
  
  readonly attribute Promise<undefined> ready;
  readonly attribute Promise<undefined> closed;

  Promise<undefined> abort(optional any reason);
  readonly attribute AbortSignal signal;
 
  readonly attribute SocketStats stats;
  readonly attribute SocketInfo info;
}

这目前还只是一个提议,在 Workers 中实际交付该功能时,详细信息很可能会与上述示例有所不同。我们希望其他平台能够加入我们,共同开发和支持这一新 API,以便开发人员拥有一个一致的构建基础,而不论其在哪里运行其代码。

引入 Socket Workers

打开套接字客户端连接的功能只是这个构想的一半。

当我们第一次谈论添加这些功能时,就提出了一个明显的问题:使用非 HTTP 协议连接 Workers 会怎么样?如果不只是能够将一个 Worker 连接到一些其他后端数据库,还可以在边缘、Worker 内部实施整个数据库,并让非 HTTP 客户端连接到它,会怎么样?对于这种情况,如果我们可以在 Workers 中实施一个 SMTP 服务器,会怎么样?或者是一个 MQTT 信息队列?一个完整的 VoIP 平台?或者是实施数据包筛选器、转换、检查器或协议转码器?

Workers 十分强大,绝不仅仅限于 HTTP 和 WebSockets,因此我们很快将引入 Socket Workers——也就是说,可以使用原始 TCP、UDP 或 QUIC 协议直接连接到 Workers,而无需使用 HTTP。

这种新的 Workers 功能会是什么样?许多细节仍在进行开发,但想法是部署一个 Worker 脚本,采用与如今“fetch”事件差不多的运作方式了解和响应“connect”事件。重要的是,这将建立在开发用于客户端连接的相同通用套接字 API 上:

addEventListener('connect', (event) => {
  const enc = new TextEncoder();
  const writer = event.socket.writable.getWriter();
  writer.write(enc.encode('Hello World'));
  writer.close();
});

后续步骤(以及行动呼吁)

用于 JavaScript 和 Socket Workers 的新套接字 API 正在积极开发中,最初的重点是提供更好更有效的方法让 Workers 连接到后端的数据库。您可以在此处注册以加入访问 Database Connectors 和 Socket Workers 的等待名单。我们很期待与早期用户以及我们的技术合作伙伴合作,以开发、优化和测试这些新功能。

一旦发布后,我们希望 Socket Workers 能够为可部署到 Cloudflare 网络边缘的智能分布式应用程序类型敞开大门,很期待看到大家使用这些功能构建应用程序的成果。

我们保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的过程

从任何设备访问 1.1.1.1,以开始使用我们的免费应用程序,帮助您更快、更安全地访问互联网。要进一步了解我们帮助构建更美好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位
Full Stack Week (CN)TCP (CN)Cloudflare Workers (CN)QUIC (CN)简体中文Product News (CN)Serverless (ZH)

在 X 上关注

Brendan Coll|@_mrbbot
Jonathan Kuperman|@jkup
Cloudflare|@cloudflare

相关帖子

2021年12月19日 13:59

在 Cloudflare 上构建您的下一个视频应用程序

过去,构建视频应用程序十分困难。在录制、编码和播放视频背后有许多复杂的技术。幸运的是,Cloudflare Stream 分担走了所有困难的部分,现在您可以轻松构建自定义视频和流媒体应用程序。让我们看一下,我们可以如何结合 Cloudflare Stream、Access、Pages 和 Workers,使用极少的代码创建一个高性能的视频应用程序。...

2021年11月20日 13:59

网络性能更新:Full Stack Week

两个多月前,我们分享了全球最后一英里网络的广泛基准测试结果。结果显示,基于一系列的测试(TCP 连接时间、接收第一个字节的时间、接收最后一个字节的时间)和一系列测量值(平均值为 p95),Cloudflare 是全球 49% 的网络中最快的提供商。从那时起,我们一直在不断努力...

2021年11月19日 14:05

喜大普奔:Cloudflare Workers 提供对 Stripe JavaScript SDK 原生支持

在应用中处理支付是建立在线业务的关键。对于许多开发者来说,处理支付的主要选择是 Stripe。自从大约七年前我第一次接触 Stripe 以来,这项服务的发展已经远远超出了简单的支付处理。在我去年分享的电子商务示例应用程序中,Stripe使用 Connect 产品管理了一个完整的卖家市场。对于那些不满足于接受支付功能的开发者来说,Stripe 的产品套件非常适用。...