WebSocketサーバーを構築する
Durable ObjectsとWorkersを使用してWebSocketサーバーを構築します。
この例では、Durable ObjectsとWorkersを使用してWebSocketサーバーを構築する方法を示します。この例では、新しいWebSocket接続を作成するためのエンドポイントを公開します。このWebSocket接続は、メッセージをエコーし、現在確立されているWebSocket接続の総数を含みます。詳細については、WebSocketsでDurable Objectsを使用するを参照してください。
import { DurableObject } from "cloudflare:workers";
// Workerexport default { async fetch(request, env, ctx) { if (request.url.endsWith("/websocket")) { // WebSocket Upgradeリクエストを受け取ることを期待します。 // もしあれば、リクエストを受け入れ、WebSocketレスポンスを返します。 const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Durable Object expected Upgrade: websocket', { status: 426 }); }
// この例では、名前「foo」がハードコーディングされているため、 // 同じDurable Objectインスタンスを参照します。 let id = env.WEBSOCKET_SERVER.idFromName("foo"); let stub = env.WEBSOCKET_SERVER.get(id);
return stub.fetch(request); }
return new Response(null, { status: 400, statusText: 'Bad Request', headers: { 'Content-Type': 'text/plain', }, }); }};
// Durable Objectexport class WebSocketServer extends DurableObject { currentlyConnectedWebSockets;
constructor(ctx, env) { // このコンストラクタが実行されるたびにリセットされます。 // 通常のWebSocketはDurable Objectのリセットを生き延びません。 // // 休眠APIを介して受け入れられたWebSocketは // 特定のタイプの追放を生き延びることができますが、 // ここではそれについては説明しません。 super(ctx, env); this.currentlyConnectedWebSockets = 0; }
async fetch(request) { // WebSocket接続の両端を作成します。 const webSocketPair = new WebSocketPair(); const [client, server] = Object.values(webSocketPair);
// `accept()`を呼び出すことで、このWebSocketが // Durable Object内でリクエストを終了することを示します。 // これは接続を「受け入れる」効果があり、 // WebSocketがメッセージを送受信できるようにします。 server.accept(); this.currentlyConnectedWebSockets += 1;
// クライアントからメッセージを受信すると、サーバーは同じメッセージで応答し、 // "[Durable Object]: "プレフィックス付きで接続の総数を返します。 server.addEventListener('message', (event) => { server.send(`[Durable Object] currentlyConnectedWebSockets: ${this.currentlyConnectedWebSockets}`); });
// クライアントが接続を閉じると、ランタイムも接続を閉じます。 server.addEventListener('close', (cls) => { this.currentlyConnectedWebSockets -= 1; server.close(cls.code, "Durable Object is closing WebSocket"); });
return new Response(null, { status: 101, webSocket: client, }); }}import { DurableObject } from "cloudflare:workers";
export interface Env { WEBSOCKET_SERVER: DurableObjectNamespace<WebSocketServer>;}
// Workerexport default { async fetch(request, env, ctx): Promise<Response> { if (request.url.endsWith("/websocket")) { // WebSocket Upgradeリクエストを受け取ることを期待します。 // もしあれば、リクエストを受け入れ、WebSocketレスポンスを返します。 const upgradeHeader = request.headers.get('Upgrade'); if (!upgradeHeader || upgradeHeader !== 'websocket') { return new Response('Durable Object expected Upgrade: websocket', { status: 426 }); }
// この例では、名前「foo」がハードコーディングされているため、 // 同じDurable Objectインスタンスを参照します。 let id = env.WEBSOCKET_SERVER.idFromName("foo"); let stub = env.WEBSOCKET_SERVER.get(id);
return stub.fetch(request); }
return new Response(null, { status: 400, statusText: 'Bad Request', headers: { 'Content-Type': 'text/plain', }, }); }} satisfies ExportedHandler<Env>;
// Durable Objectexport class WebSocketServer extends DurableObject { currentlyConnectedWebSockets: number;
constructor(ctx: DurableObjectState, env: Env) { // このコンストラクタが実行されるたびにリセットされます。 // 通常のWebSocketはDurable Objectのリセットを生き延びません。 // // 休眠APIを介して受け入れられたWebSocketは // 特定のタイプの追放を生き延びることができますが、 // ここではそれについては説明しません。 super(ctx, env); this.currentlyConnectedWebSockets = 0; }
async fetch(request: Request): Promise<Response> { // WebSocket接続の両端を作成します。 const webSocketPair = new WebSocketPair(); const [client, server] = Object.values(webSocketPair);
// `accept()`を呼び出すことで、このWebSocketが // Durable Object内でリクエストを終了することを示します。 // これは接続を「受け入れる」効果があり、 // WebSocketがメッセージを送受信できるようにします。 server.accept(); this.currentlyConnectedWebSockets += 1;
// クライアントからメッセージを受信すると、サーバーは同じメッセージで応答し、 // "[Durable Object]: "プレフィックス付きで接続の総数を返します。 server.addEventListener('message', (event: MessageEvent) => { server.send(`[Durable Object] currentlyConnectedWebSockets: ${this.currentlyConnectedWebSockets}`); });
// クライアントが接続を閉じると、ランタイムも接続を閉じます。 server.addEventListener('close', (cls: CloseEvent) => { this.currentlyConnectedWebSockets -= 1; server.close(cls.code, "Durable Object is closing WebSocket"); });
return new Response(null, { status: 101, webSocket: client, }); }}最後に、wrangler.tomlファイルを構成して、以前に選択した名前空間とクラス名に基づいてDurable Objectのバインディングと移行を含めます。
name = "websocket-server"
[[durable_objects.bindings]]name = "WEBSOCKET_SERVER"class_name = "WebSocketServer"
[[migrations]]tag = "v1"new_classes = ["WebSocketServer"]