Cloudflare Workers 打通入站 TCP:在边缘运行全双工 gRPC 服务

2026-08-03 39 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

Cloudflare Workers 与 Containers 现在可以通过 Spectrum 接收入站 TCP 连接,并将套接字直接转发给 Durable Objects 或 Containers。这个变化扩展了 Workers 的网络边界:除了处理传统 HTTP 请求,开发者还可以承载需要长连接、双向数据流和二进制协议的服务,包括全双工 gRPC 应用。

与此同时,Workers 可以完成 gRPC 到 gRPC-Web 的自动转换。这意味着浏览器客户端、边缘逻辑和后端 gRPC 服务之间可以减少一层专门维护的协议转换代理。

从 HTTP 请求处理走向套接字转发

传统 Worker 的核心抽象是一次 HTTP 请求对应一次 fetch() 调用。它适合 API 网关、缓存、鉴权和内容改写,但原始 TCP 服务的连接模型不同:

  • 连接可能持续数分钟甚至更久;
  • 客户端和服务端可以同时发送数据;
  • 协议不一定以 HTTP 请求和响应为边界;
  • 服务端可能需要保存连接状态和会话上下文。

新的入站 TCP 能力通过 Spectrum 接收外部连接,再把套接字交给 Durable Objects 或 Containers。两类运行目标适合不同的工作负载:

  • Durable Objects 适合按房间、设备、租户或会话组织有状态连接,并利用单实例协调语义管理状态。
  • Containers 适合运行已有的 gRPC 服务、原生二进制程序,或者依赖完整操作系统进程模型的应用。

这并不意味着所有 HTTP API 都应该迁移到 TCP。普通 REST API、短请求和缓存友好的读取接口,继续使用 HTTP Worker 往往更简单。入站 TCP 的价值主要体现在已有协议兼容、长连接和双向流式处理上。

gRPC 为什么是直接受益者

gRPC 基于明确的服务定义生成客户端和服务端代码,并支持一元调用、服务端流、客户端流和双向流。此前,把完整的 gRPC 服务放到边缘链路中,通常需要处理 HTTP/2、长连接以及浏览器不直接支持标准 gRPC 客户端等问题。

Workers 与 Containers 支持全双工 gRPC 后,客户端和服务端可以在同一个调用中持续交换消息。适合这一模型的场景包括:

  • 实时遥测与设备指令;
  • 持续输出的 AI 推理或任务进度;
  • 多人协作事件流;
  • 内部服务之间的低延迟 RPC;
  • 将现有 gRPC 服务迁移到更靠近用户的位置。

自动 gRPC-to-gRPC-Web 转换则面向浏览器兼容性。浏览器可以使用 gRPC-Web 客户端,而 Worker 负责与后端 gRPC 服务衔接。这样可以减少单独部署 Envoy 等转换层的需求,但上线前仍应验证元数据、状态码、流式模式和超时行为是否符合客户端预期。

可以这样实践:准备一个双向流 gRPC 服务

下面是一个可在本地运行、之后再改造成 Container 工作负载的最小 Python 示例。Cloudflare 的具体路由、Spectrum 配置和绑定名称应以账户中可用的产品配置为准;这里不假设尚未由来源说明的部署字段。

创建 chat.proto

syntax = "proto3";

package edgechat;

service ChatService {
  rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}

message ChatMessage {
  string sender = 1;
  string text = 2;
}

安装依赖并生成 Python 代码:

python -m venv .venv
. .venv/bin/activate
python -m pip install grpcio grpcio-tools
python -m grpc_tools.protoc \
  -I. \
  --python_out=. \
  --grpc_python_out=. \
  chat.proto

创建 server.py

from concurrent import futures
import grpc

import chat_pb2
import chat_pb2_grpc


class ChatService(chat_pb2_grpc.ChatServiceServicer):
    def Chat(self, request_iterator, context):
        for message in request_iterator:
            yield chat_pb2.ChatMessage(
                sender="edge-server",
                text=f"received from {message.sender}: {message.text}",
            )


def serve() -> None:
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=8))
    chat_pb2_grpc.add_ChatServiceServicer_to_server(ChatService(), server)
    server.add_insecure_port("0.0.0.0:50051")
    server.start()
    print("gRPC server listening on 0.0.0.0:50051")
    server.wait_for_termination()


if __name__ == "__main__":
    serve()

再创建 client.py,验证客户端和服务端可以在一个 RPC 中交换多条消息:

import grpc

import chat_pb2
import chat_pb2_grpc


def outgoing_messages():
    for text in ("hello", "streaming", "goodbye"):
        yield chat_pb2.ChatMessage(sender="local-client", text=text)


with grpc.insecure_channel("localhost:50051") as channel:
    client = chat_pb2_grpc.ChatServiceStub(channel)
    for reply in client.Chat(outgoing_messages()):
        print(f"{reply.sender}: {reply.text}")

分别启动服务端和客户端:

# 终端 1
. .venv/bin/activate
python server.py

# 终端 2
. .venv/bin/activate
python client.py

预期输出类似:

edge-server: received from local-client: hello
edge-server: received from local-client: streaming
edge-server: received from local-client: goodbye

容器化时,可以使用下面的 Dockerfile。部署到 Cloudflare Containers 后,再通过实际可用的 Spectrum 入站 TCP 配置把外部端口转发到容器的 50051 端口。

FROM python:3.12-slim

WORKDIR /app
COPY chat.proto server.py ./

RUN pip install --no-cache-dir grpcio grpcio-tools \
    && python -m grpc_tools.protoc \
       -I. \
       --python_out=. \
       --grpc_python_out=. \
       chat.proto

EXPOSE 50051
CMD ["python", "server.py"]

本地构建和测试:

docker build -t edge-grpc-demo .
docker run --rm -p 50051:50051 edge-grpc-demo

生产环境不应直接照搬示例中的明文连接。应在 Spectrum 和服务端链路中确认 TLS 终止方式、客户端认证以及容器内部端口是否需要加密。

浏览器接入时的边界

gRPC-Web 转换能缩短浏览器到 gRPC 服务的路径,但它不是应用层设计的替代品。接入前需要检查几个问题:

  • 浏览器客户端使用的调用模式是否受支持,尤其是双向流行为;
  • Worker 转换前后是否完整保留认证元数据;
  • gRPC 状态码和错误详情能否被前端正确解析;
  • 长连接的空闲超时、最大持续时间和重连策略;
  • 单条消息、整个流以及容器实例的资源上限;
  • 客户端断开后,服务端是否及时取消后台任务。

对于公开服务,还应在边缘执行身份验证、限流和连接数控制。TCP 连接比短 HTTP 请求占用资源更久,只限制每秒新建连接并不足够,还要限制每个租户的并发连接和流量。

上线前的选择清单

可以按工作负载决定运行位置:需要协调房间、设备或会话状态时,优先评估 Durable Objects;需要复用现有 gRPC 二进制、语言运行时或系统依赖时,Containers 通常更自然。若服务仍是无状态短请求,则没有必要仅为了使用新能力而改成 TCP。

上线前至少完成以下验证:

  • 用真实客户端测试一元调用和实际采用的流式调用;
  • 验证 TLS、认证元数据和 gRPC 状态码;
  • 对断线、重连、半关闭和服务端取消执行故障测试;
  • 测量长连接数量、消息吞吐、内存和实例扩缩容行为;
  • 为连接时长、并发连接数和单租户流量设置上限;
  • 确认 gRPC-Web 客户端所需的流式语义能够成立。

入站 TCP 让 Workers、Durable Objects 和 Containers 不再局限于请求响应式 HTTP 工作负载。真正的工程收益不只是“能打开一个端口”,而是可以把状态协调、协议转换和现有 gRPC 服务放进同一条边缘链路,同时保留对长连接资源和安全边界的明确控制。


相关推荐