✍️ 필사 모드: 2026 年の realtime web — WebSocket・SSE・WebTransport・WebRTC 深掘り比較(LLM streaming が SSE を選んだ理由まで)
日本語プロローグ — 「realtime」という言葉の罠
「realtime 機能を入れてほしい」という依頼が来る。最初に頭に浮かぶ言葉は WebSocket だ。とりあえず WS server を立て、router の後ろに sticky session を付け、token 認証を乗せる。半年後、同じ会社の別チームが LLM の token を stream していて — 不思議なことに — SSE を使っている。誰かが訊く: 「なんで WebSocket じゃないの?」
答えは単純ではない。「realtime」は単一の問題ではなく spectrum である。
- chat の 1 行を世界の 1 万人に 50 ms 以内 — 双方向・低遅延。
- LLM の token を 1 人のユーザーに 1 分間 — 一方向 stream。
- game の座標を毎秒 60 回、packet が 1 つ落ちても次が追いつくならよし — 多対多・unreliable datagram。
- 通話の video / audio に補助 data — P2P media。
- 5 分に 1 度 push を端末に — それは push であって realtime ではない。
それぞれに合う protocol が違う。この記事は 2026 年時点の 5 候補 — WebSocket、Server-Sent Events (SSE)、WebTransport、WebRTC DataChannel、long polling — を比較する。おまけに HTTP/2 Server Push がなぜ死んだか、LLM streaming のほぼ全部が SSE に収束した本当の理由まで。
TL;DR. 一方向 streaming なら SSE。双方向・低遅延なら WebSocket。game 系の unreliable multi-stream なら WebTransport。P2P や media なら WebRTC。そして — 正直に言うと — ただの polling で足りる場合も多い。
1 章 · 風景 — 2026 年の 5 つの protocol
Browser ◀──────── Server (または別の browser)
───────────────────────────────────────────────
WebSocket 双方向、TCP、frame 形式
SSE (EventSource) server から client への一方向、HTTP 上
WebTransport 双方向、QUIC/HTTP3、多重化
WebRTC DataChannel P2P、UDP/SCTP、unreliable も可
Long Polling HTTP polling、response を保留
各 protocol を 1 行で:
- WebSocket — 1 本の TCP 上に双方向 frame。2011 年標準化、最も普及、最もよく知られた落とし穴も多い。
- Server-Sent Events — HTTP response を閉じずに text を流す。
text/event-streamMIME、自動 reconnect 内蔵。一方向 (server → client)。 - WebTransport — QUIC 上の双方向。信頼 stream + unreliable datagram を 1 本の接続で。head-of-line blocking なし。Chrome stable で利用可能。
- WebRTC DataChannel — 2 つの peer 間の直接接続。P2P、任意で unreliable。運用は最難。
- Long Polling — 古い友人。response を保留してイベント発生時に応答。あらゆる環境で動く。
そして死者: HTTP/2 Server Push. Chrome が 2022 年に無効化し、仕様からの削除が進行中。cache 動作が微妙で、実利益が小さかった。
2 章 · WebSocket — まだ働き者、まだ落とし穴
WebSocket は 2011 年に RFC 6455 として標準化された。要点は単純だ: HTTP/1.1 の Upgrade ヘッダで接続を開始し、以後は TCP 上で独自 frame (text / binary / Ping / Close) を双方向に流す。
頭の中の模型
Client Server
│ GET /ws HTTP/1.1 │
│ Upgrade: websocket │
│ ──────────────────────────────▶ │
│ │
│ ◀────────────────────────────── │
│ HTTP/1.1 101 Switching │
│ │
│ ◀══════ frame ══════▶ │ (双方向)
│ ◀══════ frame ══════▶ │
│ │
│ Close 0x88 │
Upgrade handshake が終わるとその TCP 接続はもはや HTTP ではない。だから router・load balancer・proxy・CDN の挙動が微妙になる。
強み
- 真の 双方向。client もいつでも送れるし、server もいつでも送れる。
- 低遅延。handshake は 1 回、以降の frame overhead は 2〜14 byte。
- binary 安全。game や binary protocol に直接使える。
- ほぼ全ての browser・言語・SDK が対応。
落とし穴
- HTTP/2 ではない。 WS 自体は HTTP/1.1 の upgrade。RFC 8441 で HTTP/2 上 WebSocket が可能だが server 実装が一様でない。
- sticky session が必須。 WS 接続は同じ backend インスタンスに固定される必要があり、Pub/Sub のような fan-out 層が要る。
- head-of-line blocking。 1 本の TCP なので、1 つの大きな message が後続を塞ぐ。
- 自動 reconnect なし。 自分で書く — exponential backoff、再送 queue、sequence number。
- proxy 非互換。 一部の企業 proxy は WS handshake を切る。
- HTTP cache を回避。 GET ではないので CDN cache の恩恵なし。
小さな chat server (Node-ish + ws)
// server.ts — minimal WS echo + broadcast
import { WebSocketServer } from 'ws'
const wss = new WebSocketServer({ port: 8080 })
const clients = new Set<WebSocket>()
wss.on('connection', (ws) => {
clients.add(ws)
ws.send(JSON.stringify({ type: 'welcome', count: clients.size }))
ws.on('message', (raw) => {
const msg = JSON.parse(raw.toString())
for (const c of clients) {
if (c !== ws && c.readyState === 1) {
c.send(JSON.stringify({ ...msg, ts: Date.now() }))
}
}
})
ws.on('close', () => clients.delete(ws))
})
client 側:
const ws = new WebSocket('wss://example.com/ws')
ws.onopen = () => ws.send(JSON.stringify({ type: 'hello', name: 'Jun' }))
ws.onmessage = (e) => console.log('recv:', JSON.parse(e.data))
ws.onclose = () => {
// reconnect ロジックは自分で書く — backoff、jitter、token refresh、...
}
これで終わりではない。production では — 認証・heartbeat・reconnect・message queue・rollout 時の graceful drain — まで含めて数百行に育つ。
3 章 · Server-Sent Events — LLM streaming が選んだ伏兵
SSE は 2009 年に HTML5 仕様に入った。あまりに単純で長らく軽く見られていたが、2024〜2026 年の LLM streaming brew によって 事実上の標準 になった。
頭の中の模型
Client Server
│ GET /stream HTTP/1.1 │
│ Accept: text/event-stream │
│ ──────────────────────────────────▶ │
│ │
│ ◀────────────────────────────────── │
│ HTTP/1.1 200 OK │
│ Content-Type: text/event-stream │
│ │
│ ◀── data: {"token":"hello"}\n\n │
│ ◀── data: {"token":" world"}\n\n │
│ ◀── data: [DONE]\n\n │
│ │
│ (response が閉じない) │
接続はただの HTTP response。server は response を閉じずに data: ...\n\n 形式で 1 行ずつ流す。client は EventSource で自動 parse・自動 reconnect。
強み
- 単純。 HTTP のまま。header・method・cache 規則そのまま。
- 自動 reconnect。
EventSourceが切れたら自動で再接続、Last-Event-IDで resume。 - firewall friendly。 普通の HTTP、どんな proxy も通過。
- HTTP/2・HTTP/3 を自動で活用。 多重化もタダ。
- 一方向 = server logic が単純。 Pub/Sub fan-out に自然に合う。
- CDN・gzip 互換。 response であって upgrade ではないため。
弱み
- 一方向のみ。 client → server は別の POST が要る (それが LLM API の形)。
- text 専用。 UTF-8 text のみ — binary は base64 で非効率。
- 接続数の上限。 HTTP/1.1 では origin あたり 6 — HTTP/2 ではほぼ無制限。
- timeout。 一部の proxy (特に nginx の default) は idle response を切る — 定期的な keep-alive コメントが要る。
なぜ LLM streaming が SSE を選んだか
OpenAI、Anthropic、Google Gemini、Cohere — ほぼ全ての LLM token streaming API が SSE。出典: OpenAI Streaming docs https://platform.openai.com/docs/api-reference/streaming、Anthropic Messages Streaming https://docs.anthropic.com/en/api/messages-streaming。理由は 7 つ:
- 本質的に一方向。 prompt は 1 回の POST、答えは token ごとに流れる。WS の双方向は不要。
- retry が自然。 通常の HTTP retry 方針 (429・5xx) がそのまま動く。
- 認証・rate limit・課金が標準 HTTP middleware に乗る。 WS は毎回自前。
- firewall・proxy 通過。 企業網で WS は塞がれることがある。
- CDN と相性。 Cloudflare・Vercel Edge Functions が SSE を一級でサポート。
- test が簡単。
curl -Nで終わり。 - HTTP/2・HTTP/3 で多重化が無料。 同時 stream が 1 本の接続を共有。
WebSocket を使うとこの 7 つが全て自前項目になる。
30 行 token streamer (Bun / Node)
// app/api/chat/route.ts — 30 行 SSE token streamer
export const runtime = 'edge'
export async function POST(req: Request) {
const { prompt } = await req.json()
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
const send = (data: unknown) => {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
}
// 仮想 LLM: 50 ms ごとに token を送る
const tokens = ['こん', 'にちは', '、', ' SSE', ' demo', ' です', '。']
for (const t of tokens) {
send({ token: t })
await new Promise((r) => setTimeout(r, 50))
}
send('[DONE]')
controller.close()
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
Connection: 'keep-alive',
},
})
}
client 側 — EventSource は POST を受けないので、fetch streaming または SSE parser (例: eventsource-parser) を使う。
const res = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ prompt: 'hi' }),
headers: { 'Content-Type': 'application/json' },
})
const reader = res.body!.getReader()
const decoder = new TextDecoder()
let buf = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buf += decoder.decode(value, { stream: true })
for (const line of buf.split('\n\n')) {
if (line.startsWith('data: ')) console.log(line.slice(6))
}
buf = ''
}
これが ChatGPT・Claude UI の token streaming の正体だ。40 行で終わる。
4 章 · WebTransport — HTTP/3 時代の新しい働き者
WebTransport は IETF が 2024 年に RFC 9484 として標準化した新 protocol。QUIC 上の双方向 + datagram を 1 本の接続で提供する。出典: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API。
頭の中の模型
Browser ──────── QUIC (UDP 上) ──────── Server
│
├── 双方向の信頼 stream #1
├── 双方向の信頼 stream #2
├── 単方向の信頼 stream #3
└── unreliable datagram (UDP のように)
1 本の QUIC 接続上に複数 stream。ある stream の packet loss が他を塞がない — head-of-line blocking がない。WebSocket の最大の弱点が消える。
2026 年 browser サポート
- Chrome / Edge — stable 対応 (Chrome 97+、2022 年から)。https://chromestatus.com/feature/4854541929873408
- Firefox — stable 対応 (Firefox 114+、2023 年から)。default 有効化は作業中。
- Safari — 2026 年 5 月時点で部分対応 (Safari Technology Preview で実験的)。stable の全面有効化はまだ。
server 実装: aioquic (Python)、quinn (Rust)、msquic (Microsoft)、Cloudflare Workers WebTransport API。出典: https://github.com/aiortc/aioquic。
強み
- head-of-line blocking なし。 game や multi-stream content に決定的。
- 信頼・unreliable の両方。 unreliable datagram は game 座標・VOIP 補助 track に最適。
- 接続 migration。 QUIC 特性 — Wi-Fi → cellular で IP が変わっても接続が続く。
- TLS 1.3 必須。 security が default。
弱み
- Safari 未完。 全ユーザー対象なら fallback が必要。
- 運用 tool 未成熟。 debugger・logging・metrics の生態系が WS ほど豊富でない。
- UDP 遮断。 企業網・キャリア網の一部で UDP が塞がれて fallback が必要。
- 証明書要件。 ドメイン証明書が要る (自己署名なら
serverCertificateHashesで pin)。
datagram demo
// game 座標を 30 fps で unreliable datagram として送る
const transport = new WebTransport('https://game.example.com/wt')
await transport.ready
const writer = transport.datagrams.writable.getWriter()
const reader = transport.datagrams.readable.getReader()
setInterval(async () => {
const buf = new ArrayBuffer(8)
const view = new DataView(buf)
view.setFloat32(0, player.x)
view.setFloat32(4, player.y)
await writer.write(new Uint8Array(buf))
}, 33)
;(async () => {
while (true) {
const { value, done } = await reader.read()
if (done) break
const view = new DataView(value.buffer)
other.x = view.getFloat32(0)
other.y = view.getFloat32(4)
}
})()
これを WebSocket で同じことをしようとすると、座標 1 packet が遅れたら後続が塞がる。WebTransport はただの新しい datagram。落ちたら落ちたまま、次へ。
5 章 · WebRTC DataChannel — P2P の王、運用の地獄
WebRTC は本来 video call 用に作られた。RTCDataChannel は同じ P2P 接続上に任意の data を流せる兄弟 API。
頭の中の模型
Peer A Peer B
│ (signaling server) │
│ ───── SDP offer ────────▶ │
│ ◀──── SDP answer ───────── │
│ ───── ICE candidates ───▶ │
│ ◀──── ICE candidates ───── │
│ │
│ ╔════ P2P SCTP/UDP ════╗ │
│ ║ DataChannel ║ │
│ ║ Video/Audio ║ │
│ ╚══════════════════════╝ │
要点は signaling が別 であること。WebRTC は 2 peer の IP・port・media 能力を交渉する間、message を運ぶ channel が要る — それが signaling server (通常 WebSocket か SSE)。
強み
- 真の P2P。 server を経由しないので遅延が最低、server 帯域も不要。
- unreliable mode。
ordered: false, maxRetransmits: 0で UDP 的に。 - NAT 越え。 STUN・TURN 基盤で NAT 越しの user も繋ぐ。
- media と一体。 video call に data channel を 1 本の接続で乗せられる。
弱み
- 運用が最難。 signaling server + STUN server + TURN server (NAT 越え失敗時の fallback) — 3 つの基盤。
- TURN は P2P ではない。 TURN に fallback すると実質 server relay で、帯域・課金が暴走。
- 接続時間が長い。 数百 ms〜数秒の ICE 交渉。
- browser 互換性の細部差異。 Chrome / Firefox / Safari が端で違う動き。
使いどころ
- video / voice 通話に付随する data (chat・file 転送・game 入力)。
- 真の P2P game (server cost 削減、遅延最小化)。
- file 共有 (WebTorrent 系)。
- metaverse / VR — 位置・表情など高頻度 data。
LLM token streaming に WebRTC を使う人はいない。その刃は用途が狭く、運用負担が重い。
6 章 · long polling — 古い友、死なず
browser は HTTP/1.1 の時代から polling をしてきた。その進化が long polling だ。client が GET を送ると、server は応答を 即座に返さず イベント発生まで保留する。イベントが起きたら応答し、client は即座に再度 GET。
頭の中の模型
Client Server
│ GET /events │
│ ──────────────────────────▶ │
│ │ (event 待機、保留)
│ │
│ │ event 発生
│ ◀────── 200 OK [event] ───── │
│ GET /events?since=... │
│ ──────────────────────────▶ │
│ │ (また待機)
単純で、動かない環境がない。
強み
- どこでも動く。 HTTP/1.0 でも。あらゆる proxy が通す。
- 認証・CORS・cache・CDN がすべて標準 HTTP。
- state が少ない。 1 request 1 response なので server を stateless に保てる。
- fallback として最強。 WS・SSE・WT が全部失敗しても polling は動く。
弱み
- request overhead。 イベントごとに HTTP header の代金。
- 遅延が揺れる。 response 後、次の GET までに gap がある。
- timeout の調整。 長すぎれば proxy が切り、短すぎれば普通の polling になる。
2026 年の現実
まだ生きている。特に — mobile SDK の fallback、社内 LAN chat、通知 system — で。SSE が使えない環境 (一部の embedded browser など) でも。「cool でない」と切り捨ててはいけない。
7 章 · 死者 — HTTP/2 Server Push の短い生涯
HTTP/2 Server Push は 2015 年に登場した。きれいな idea だった — client が要求する前に server がリソースを push、page が速くなる。
現実は失望だった。Chrome の telemetry (2020〜2021) では大多数の push が — 既に cache にあるので — 無駄だった。cache 挙動が微妙で、実装が一様でなかった。Chrome は 2022 年に無効化。出典: https://developer.chrome.com/blog/removing-push。仕様からも削除中。
代替。 真のリソース push なら 103 Early Hints + link rel=preload。message push なら SSE。
8 章 · 意思決定 matrix
| use case | 第 1 候補 | 代替 | 備考 |
|---|---|---|---|
| LLM token streaming | SSE | WS | server → client 一方向、retry / CDN friendly |
| chat (1:1、1:多) | WebSocket | SSE + POST | 双方向・低遅延 |
| 実時間 multiplayer game | WebTransport | WebRTC | unreliable datagram、no HoL blocking |
| dashboard / 実時間 metrics | SSE | WS | 一方向、自動 reconnect |
| 協調 editor (CRDT) | WebSocket | WT | 双方向 + 順序 |
| 通話 | WebRTC | — | media と data を 1 channel |
| P2P file 転送 | WebRTC | WS relay | server 帯域節約 |
| 通知 (低頻度) | long polling | SSE | server cost と複雑度を最小化 |
| 株 / coin ticker (broadcast) | SSE | WS | 一方向、fan-out が単純 |
| IoT 命令 (双方向) | WebSocket | WT | 小さな payload、双方向 |
| 最終 fallback | long polling | — | あらゆる環境を通る |
この表は正解ではなく 出発点。決定にはトレードオフがあり、team の運用能力・既存 infra・user の環境が表を揺らす。
9 章 · 正直な意思決定 framework
5 つの質問を順に。
Q1. 真に双方向か、実は一方向か?
LLM の answer は 一方向。prompt は 1 回の POST、answer は token stream。chat message も実は — 送信は POST で完結、受信だけが stream — と見ると一方向。
真の双方向 は 2 方向で同時に message が流れる場合 — 協調 editor の CRDT 更新、multiplayer game の入力 / 状態、IoT の双方向命令。
一方向なら SSE をまず検討。cost・運用・複雑度が圧倒的に低い。
Q2. 信頼が必要か、unreliable でよいか?
chat message・決済 event・状態更新 — 信頼必須。TCP・QUIC stream。
game 座標・VOIP・実時間 sensor — unreliable OK。1 packet 落ちても次が追いつけば終わり。UDP・QUIC datagram。
unreliable が要るなら WebTransport (または WebRTC)。WS には unreliable mode がない。
Q3. P2P が本当に要るか?
P2P は — 本当に — server を経由しない場合だけ。通話、file 転送、真の P2P game。それ以外は client-server が圧倒的に単純。
P2P を決めたら STUN + TURN の基盤 cost・運用を受け入れる覚悟。TURN に fallback したらそれは実質 relay で、P2P の利点は消える。
Q4. 既存 infra は?
会社が既に Kafka + Pub/Sub + nginx で SSE を回しているなら — 新機能に WS を入れない。運用 team が両方を知らないといけなくなる。
WS を既にうまく運用していて、新機能に SSE を導入するなら理にかなう — 2 つの protocol が違う問題を解く場合に限る。
Q5. user の network 環境は?
企業網・キャリア網には — UDP 遮断、WS 遮断、idle response 遮断 — 何でもある。user の環境が多様なら fallback は必須。
WT 試行 ──失敗──▶ WS 試行 ──失敗──▶ SSE 試行 ──失敗──▶ long polling
production の realtime stack はだいたいこの fallback chain を何らかの形で持つ。
10 章 · 実例 — 誰が何を使うか
- ChatGPT、Claude、Gemini API の token streaming — SSE。出典: OpenAI https://platform.openai.com/docs/api-reference/streaming、Anthropic https://docs.anthropic.com/en/api/messages-streaming。
- Slack message — 歴史的に WebSocket。2025 年頃 mobile は HTTP polling + push 通知に移行 (battery のため)。
- Discord — WebSocket (message) + WebRTC (voice / video)。出典: https://discord.com/developers/docs/topics/gateway。
- Figma の同時 editing — WebSocket + 自前 CRDT。
- Google Docs — long polling から始まり、WebSocket へ移行。
- Cloudflare Stream / Workers — WebTransport 対応、game / live video 系の load を受ける。
- Zoom — WebRTC (media) + 自前 signaling。
- GitHub Live Updates — SSE。
- Twitch IRC (chat) — TCP + 自前 protocol、web client は IRC を WebSocket の上に乗せる。
- 株式取引 platform — ほぼ WebSocket (broadcast + 注文) または 自前 binary TCP (desktop)。
パターンが見える — 一方向 streaming は SSE、双方向 chat は WS、media は WebRTC、game は段階的に WT。
11 章 · 運用 note — 実際に痛い所
sticky session
WS・WT は client が同じ backend インスタンスに固定される。ALB・Envoy・HAProxy の sticky session 設定が要る。SSE は — response が最後まで 1 インスタンスで流れるが — reconnect ごとに別 インスタンスでも OK (server が stateless なら)。
graceful shutdown
deploy で WS 接続を切ると 1 万人の user が同時に再接続を試みて thundering herd。決まったパターン:
- 新規接続を拒否 (health check fail)。
- 既存接続に「すぐ切る」message を送る。
- client が jitter を入れて自発的に reconnect。
- 30〜60 秒後に強制 close。
SSE も同じ問題。long polling は — 1 request が短いので — 運がよい。
認証
WS の Upgrade handshake は HTTP なので cookie・header・token を送れる。ただし — 一部の library は handshake 後に認証を確認 — このパターンは危険。handshake で認証する。
WT は証明書ベース + header。SSE は — ただの HTTP なので — 最も単純。
WebRTC は signaling server で認証。P2P 接続自体は DTLS-SRTP で暗号化。
cost
- WS・SSE — server compute は接続数に比例。帯域は message 量に比例。
- WT — 同様だが QUIC の CPU overhead が若干高い (2026 年時点)。
- WebRTC — server cost は signaling・STUN・TURN。TURN 使用が料金 bomb。
- long polling — 保留 request あたり CPU・memory + イベントごとの HTTP header overhead。
大規模 (接続 10 万以上) では — 接続あたり memory — が決定的。Node WS は約 30〜50 KB / 接続、Go・Rust は 5〜15 KB。言語選択が運用 cost を左右する。
monitoring
- WS — 接続数、message throughput、平均接続寿命、reconnect 頻度。
- SSE — active response 数、response 寿命、encoding error。
- WT — active session、stream あたり throughput、datagram loss 率。
- WebRTC — ICE 成功率、TURN fallback 率、media RTT。
- long polling — RPS、保留時間 distribution。
OpenTelemetry のサポートは — WS 部分、SSE 完全、WT は 2026 年時点で進行中 — と異なる。
12 章 · 自作する WT datagram server (Node、aiortc なし)
browser 側 demo は上にあり、server は Rust の wtransport か Python の aioquic が最も一般的。Node では 2026 年 5 月現在、@fails-components/webtransport が最も成熟。
// server.ts — minimal WebTransport datagram echo
import { Http3Server } from '@fails-components/webtransport'
import { readFileSync } from 'node:fs'
const server = new Http3Server({
port: 4433,
host: '0.0.0.0',
secret: 'demo',
cert: readFileSync('./cert.pem'),
privKey: readFileSync('./key.pem'),
})
server.startServer()
const stream = await server.sessionStream('/wt')
const reader = stream.getReader()
while (true) {
const { value: session, done } = await reader.read()
if (done) break
await session.ready
const dgReader = session.datagrams.readable.getReader()
const dgWriter = session.datagrams.writable.getWriter()
;(async () => {
while (true) {
const { value, done } = await dgReader.read()
if (done) break
// echo
await dgWriter.write(value)
}
})()
}
demo 用の証明書が要り (browser に自己署名を受けさせるには serverCertificateHashes の pin)、local test 用に Chrome flag を有効化。
13 章 · anti-pattern 10 個
- すべての realtime 要件に反射的に WebSocket。 一方向なら SSE が単純・安価。
- 認証を handshake 後に。 WS upgrade で認証しないと一瞬未認証の接続を受ける。
- 自動 reconnect を書かない。 mobile network で切断は日常。
- heartbeat なし。 idle proxy が 30 秒後に切るのに気づかない。
- message sequence ID なし。 reconnect 後の重複・欠落を検出できない。
- graceful shutdown なしで再 deploy。 自爆 DDoS の thundering herd。
- 巨大な単一 message。 WS の head-of-line blocking を自分で起こす。
- WebRTC を P2P でない用途に。 client-server なら WS・WT が単純。
- HTTP/2 Server Push を復活させようとする。 死者だ。
- fallback chain なし。 user の 30 % が企業 proxy の後ろにいるかもしれない。
14 章 · 2026 年予測と未来
- WebTransport — Safari が stable で default 有効化する瞬間 (2026〜2027 予想) に game / media 系の default 候補になる。
- HTTP/3 + SSE — 多重化と 0-RTT 再接続が結合し、SSE がさらに強くなる。
- MoQ (Media over QUIC) — IETF MoQ working group が live media 転送の標準を作っている。出典: https://datatracker.ietf.org/wg/moq/about/。
- edge runtime での SSE 一級サポート — Vercel・Cloudflare・Deno Deploy が SSE を優先。
- WebSocket over HTTP/3 — RFC 9220、実 adoption はまだゆるやかだが進行中。
- AI agent workflow — token streaming は SSE、tool 呼び出し結果は SSE event type で分離する pattern が広がる。
LLM 時代以降の realtime web は — 1 つの protocol が全部を解くのではなく — use case ごとに最適 tool を選ぶ時代 だ。WS が全てを解いていた 2010 年代は終わった。
エピローグ — protocol は道具だ
この記事の 1 行要約: 「realtime」という言葉を見たらまず双方向か一方向かを問い、次に信頼 / unreliable を問い、次に P2P かを問う。3 つの問いと 4〜5 行で答えが出る。
LLM token streaming が SSE を選んだのは SSE が「最強」だからではない。その問題に対して SSE が最も単純な答え だからだ。同じ論理で、協調 editor は WebSocket が最も単純、game 座標は WebTransport が最も自然。
道具を愛するな、問題を愛せ。
14 項目 checklist
- 双方向は本当に必要か、実は一方向か?
- 信頼必須か、unreliable で良いか?
- P2P は本当に要るか?
- user の network 環境 (企業網・mobile・海外) を想定したか?
- fallback chain があるか?
- 認証を handshake で行うか?
- 自動 reconnect + backoff + jitter があるか?
- heartbeat / idle ping があるか?
- message sequence ID と resume 仕組みがあるか?
- graceful shutdown 手順があるか?
- sticky session / session affinity が router に設定されているか?
- 接続あたり memory cost を測ったか?
- monitoring / SLO が定義されているか?
- (LLM なら) SSE をまず検討したか?
anti-pattern 10 個 (要約)
- 一方向に WebSocket。
- 認証が遅い。
- reconnect なし。
- heartbeat なし。
- sequence ID なし。
- graceful shutdown なし。
- 巨大単一 message。
- WebRTC を P2P でない用途に。
- HTTP/2 Server Push 復活の試み。
- fallback chain なし。
次の記事予告
候補: edge runtime 上の SSE — Vercel / Cloudflare / Deno での LLM token streaming pattern 比較、MoQ (Media over QUIC) 入門 — 次世代 live media、CRDT + WebSocket — 協調 editor の運用 note。
"realtime web の次の 10 年は 1 つの protocol ではなく — 問題に合う道具を選ぶ — 時代だ。一方向なら SSE、双方向なら WebSocket、unreliable multi-stream なら WebTransport、P2P なら WebRTC。そしてそれでも足りなければ long polling はまだ生きている。"
— 2026 realtime web 深掘り、終わり。
参考 / References
- WebSocket: RFC 6455 — https://datatracker.ietf.org/doc/html/rfc6455
- WebSocket over HTTP/2: RFC 8441 — https://datatracker.ietf.org/doc/html/rfc8441
- WebSocket over HTTP/3: RFC 9220 — https://datatracker.ietf.org/doc/html/rfc9220
- SSE — HTML Living Standard: https://html.spec.whatwg.org/multipage/server-sent-events.html
- EventSource MDN: https://developer.mozilla.org/en-US/docs/Web/API/EventSource
- WebTransport: RFC 9484 — https://datatracker.ietf.org/doc/html/rfc9484
- WebTransport MDN: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
- WebTransport Chrome status: https://chromestatus.com/feature/4854541929873408
- WebRTC MDN: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API
- HTTP/2 Push removal: https://developer.chrome.com/blog/removing-push
- OpenAI Streaming API: https://platform.openai.com/docs/api-reference/streaming
- Anthropic Messages Streaming: https://docs.anthropic.com/en/api/messages-streaming
- Discord Gateway: https://discord.com/developers/docs/topics/gateway
- aioquic Python QUIC/HTTP3: https://github.com/aiortc/aioquic
- Cloudflare WebTransport: https://blog.cloudflare.com/webtransport-now-supported-in-workers/
- IETF MoQ Working Group: https://datatracker.ietf.org/wg/moq/about/
현재 단락 (1/393)
「realtime 機能を入れてほしい」という依頼が来る。最初に頭に浮かぶ言葉は WebSocket だ。とりあえず WS server を立て、router の後ろに sticky session ...