リアルタイム通信
Speakeasy のリアルタイム通信は Cloudflare Durable Objects と WebSocket Hibernation API を組み合わせて実現しています。
アーキテクチャ
Section titled “アーキテクチャ”WebSocket 接続
Section titled “WebSocket 接続”接続 URL
Section titled “接続 URL”wss://fizz.plfumg.com/ws?userId={userId}&userType={shop|customer}&shopId={shopId}&contactId={contactId}| パラメータ | 必須 | 説明 |
|---|---|---|
userId | o | ユーザー ID |
userType | o | shop または customer |
shopId | - | 店舗ユーザーの場合の店舗 ID |
contactId | - | 顧客ユーザーの場合の連絡先 ID |
接続シーケンス
Section titled “接続シーケンス”UserInbox Durable Object
Section titled “UserInbox Durable Object”ユーザーごとに1つのインスタンスが作成される WebSocket ブローカーです。
| 項目 | 設定 |
|---|---|
| ID 生成 | idFromName(userId) — 決定論的 |
| 多重接続 | 同一ユーザーの複数セッション対応 |
| Hibernation | WebSocket 非アクティブ時にメモリ解放 |
| 自動応答 | ping メッセージを DO 起動なしで応答 |
セッション管理
Section titled “セッション管理”各 WebSocket 接続にはセッション ID が付与され、接続情報がシリアライズされます。
interface WsAttachment { readonly sessionId: string; readonly subscribedRooms: readonly string[]; readonly userId: string; readonly userType: 'shop' | 'customer'; readonly shopId?: string; readonly contactId?: string;}メッセージタイプ
Section titled “メッセージタイプ”クライアント → サーバー
Section titled “クライアント → サーバー”| type | 説明 |
|---|---|
subscribe | ルームの購読開始。rooms: string[] |
unsubscribe | ルームの購読解除。rooms: string[] |
ping | ハートビート(自動応答) |
サーバー → クライアント
Section titled “サーバー → クライアント”| type | 説明 |
|---|---|
connected | 接続確認 |
message | 新着メッセージ通知 |
read_receipt | 既読通知 |
availability_update | 受付ステータス変更 |
Hibernation API
Section titled “Hibernation API”WebSocket がアイドル状態になると、Durable Object はメモリから解放されます。
// 自動応答の設定(DO を起動せずに応答)this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair('ping', 'pong'));
// WebSocket メタデータの永続化ws.serializeAttachment(attachment);
// DO 再起動時のメタデータ復元const attachment = ws.deserializeAttachment() as WsAttachment;StoreOnlineUsers Durable Object
Section titled “StoreOnlineUsers Durable Object”全オンラインユーザーを管理するグローバルシングルトンです。
| 項目 | 設定 |
|---|---|
| ID 生成 | idFromName('global') — 単一インスタンス |
| ストレージ | DO Storage(再起動時も永続化) |
| アラーム | 自動 Away 遷移用 |
オンラインユーザーデータ
Section titled “オンラインユーザーデータ”interface OnlineUser { readonly userId: string; readonly userType: 'shop' | 'customer'; readonly shopId?: string; readonly contactId?: string; readonly inboxId: string; // UserInbox DO の ID readonly connectedAt: number;}メッセージルーティング
Section titled “メッセージルーティング”新しいチャットメッセージが送信されると、StoreOnlineUsers がルーム参加者を特定し、対応する UserInbox DO にメッセージを転送します。
自動 Away 遷移
Section titled “自動 Away 遷移”店舗スタッフが全員オフラインになった場合、5分後に自動的に受付ステータスを away に変更します。
条件:
shop.availabilityMode === 'auto'の場合のみshop.chatAvailability === 'online'の場合のみ- Away 遷移時に全顧客に
availability_updateを送信
クライアント側の実装
Section titled “クライアント側の実装”Counter アプリ
Section titled “Counter アプリ”Counter の +layout.svelte で WebSocket 接続を管理します。
接続タイミング: userId と shopId が利用可能になった時ハートビート: 30秒間隔で ping 送信再接続: 指数バックオフ(3秒遅延、最大10回)購読: 全ルーム ("*") をワイルドカード購読メッセージ受信時の処理
Section titled “メッセージ受信時の処理”| メッセージタイプ | 処理 |
|---|---|
connected | 全ルーム購読リクエスト送信 |
message (顧客から) | トースト通知を表示 |
message (notification) | 通知ストアに追加 |
availability_update | 受付ステータス更新 |
read_receipt | 既読状態を UI に反映 |
- チャットルーム - ルーム管理の詳細
- メッセージ - メッセージの送受信
- 既読管理 - 未読カウント
- Fizz RPC メソッド - WebSocket 関連 RPC