Cloudflare Workers WebSocket
最后一篇讲实时通信。WebSocket 是实现聊天、通知推送、在线协作的技术基础,也是 Workers 里公认的难点。它和 Durable Objects 是黄金搭档,学完这一篇,本教程的进阶部分就收官了。
什么是 WebSocket
普通 HTTP 请求是”一问一答”,浏览器发请求,服务器返回结果,连接就断开了。要实现实时消息,传统做法是轮询,浏览器每隔几秒反复问”有新消息吗”,浪费流量且不够及时。
WebSocket 建立一条全双工长连接,连接建立后,服务器和浏览器可以随时互相推送数据,不用反复建立连接
| 对比项 | HTTP 轮询 | WebSocket |
|---|---|---|
| 连接方式 | 每轮询问都新建连接 | 一条连接长期保持 |
| 消息方向 | 只能浏览器先发 | 双向随时推送 |
| 实时性 | 取决于轮询间隔 | 毫秒级 |
| 典型场景 | 普通接口 | 聊天、推送、在线协作 |
为什么 Workers 需要 DO
WebSocket 连接是长连接,需要服务器侧持续保存”这个连接是谁”的状态。而普通 Worker 是无状态的,一个请求执行完资源就可能被回收,连接没人接管。
DO 实例可以长期存活并持有状态,正好承接 WebSocket 连接。连接的请求被转发给 DO 实例,之后这条连接的所有消息都由同一个实例处理,实例里还能存房间成员列表等状态。
握手流程
WebSocket 连接不是凭空建立的,要先通过 HTTP 完成”升级”握手
- 浏览器发送带 Upgrade 头的 HTTP 请求
- 服务器同意后返回 101 状态码
- 连接升级为 WebSocket 通道,双方开始双向通信
101 状态码就是 WebSocket 协议的标志,后面代码里会看到。
服务端实现
先定义一个聊天室的 DO 类,负责接收连接和广播消息
// src/index.js
export class ChatRoom {
constructor(state, env) {
this.state = state;
this.connections = new Set();
}
// 处理握手请求
async fetch(request) {
// 生成一对客户端和服务端 WebSocket
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
// 接管这条服务端连接
this.state.acceptWebSocket(server);
this.connections.add(server);
server.send("欢迎加入聊天室");
return new Response(null, { status: 101, webSocket: client });
}
// 收到客户端消息时触发
async webSocketMessage(ws, message) {
const text = String(message);
// 广播给房间内其他连接
for (const conn of this.connections) {
if (conn !== ws) {
conn.send(text);
}
}
}
// 连接关闭时触发
async webSocketClose(ws, code, reason, wasClean) {
this.connections.delete(ws);
}
}
几个关键 API 要理解
| API | 作用 |
|---|---|
new WebSocketPair() |
生成客户端/服务端两个成对的对象 |
state.acceptWebSocket(server) |
接管连接,之后消息才能触发回调 |
ws.send(msg) |
向这个连接推送消息 |
webSocketMessage |
收到消息的回调,参数是消息内容 |
webSocketClose |
连接关闭的回调,用来清理状态 |
再写 Worker 入口,把 /ws 路径的请求转发到聊天室 DO
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === "/ws") {
// 按房间名获取稳定的 DO 实例
const id = env.CHAT_ROOM.idFromName("room-1");
const stub = env.CHAT_ROOM.get(id);
return stub.fetch(request);
}
return new Response("请访问 /ws 建立 WebSocket 连接");
},
};
最后在 wrangler.toml 注册 DO 类
name = "my-worker"
main = "src/index.js"
compatibility_date = "2026-01-01"
[durable_objects]
bindings = [{ name = "CHAT_ROOM", class_name = "ChatRoom" }]
[[migrations]]
tag = "v1"
new_sqlite_classes = ["ChatRoom"]
部署后访问 wss://my-worker.你的子域.workers.dev/ws(注意协议是 wss),不同浏览器打开多个标签页,任何一个发消息,其他标签页都能实时收到。
客户端实现
浏览器端的代码很短,原生 WebSocket API 即可
<script>
const ws = new WebSocket("wss://my-worker.你的子域.workers.dev/ws");
// 连接建立
ws.onopen = () => {
console.log("连接成功");
};
// 收到消息
ws.onmessage = (event) => {
console.log("收到消息", event.data);
};
// 发送消息
function send(text) {
ws.send(text);
}
// 连接关闭
ws.onclose = () => {
console.log("连接关闭");
};
</script>
WebSocket 休眠(Hibernation)
每个 WebSocket 连接都会占用 DO 实例的内存。连接很多、消息很少时,让所有空闲连接一直占着内存不划算。平台提供了休眠机制,空闲连接被挂起时不占用实例资源,收到消息时自动唤醒并触发回调。
使用休眠机制后,webSocketMessage 等回调的写法不变,平台内部自动完成挂起和唤醒,对开发者透明。生产环境的高并发实时应用基本都会开启,新手了解存在即可,需要时查官方文档配置。
限制与计费
| 事项 | 说明 |
|---|---|
| 连接计费 | 每次握手算 1 次请求,之后的消息不再计费 |
| 单 Worker 连接数 | 约 1000 个,受计划限制 |
| 空闲超时 | 免费和 Pro 计划约 100 秒无消息会断开 |
| 消息大小 | 有限制,大消息建议分片 |
适用场景
| 场景 | 说明 |
|---|---|
| 实时聊天 | 聊天室、客服对话 |
| 消息推送 | 订单状态、通知提醒 |
| 在线协作 | 多人白板、协同编辑 |
| 实时数据 | 行情推送、游戏状态 |
到这里,本教程的 13 篇全部结束。回顾一下完整知识地图,入门概念、快速部署、本地开发、核心编程模型、路由与 API、环境变量、KV、D1、定时任务、调试部署、DO、WebSocket,已经覆盖了 Cloudflare Workers 最主要的知识点。剩下的进阶内容(R2、Queues、Workers AI、Hono 框架)建议在实战中按需学习,遇到问题再查官方文档。