テレビのアニメはどこから来る? / 第80話

第80話:イトは、0.05秒おきに聞かなかった|WebSocketは何を開く?

ピコルート第80話。500 msでは遅く、50 msでは空振りが増える。イトはWebSocketを開き、変化時だけ届く通信と切断後の復旧を試します。

WebSocketポーリング双方向通信切断と再接続10分更新

第79話の二枚のevent cardを片づけても、机の上には十二枚の白い票が残った。

どれも、viewerからserverへ送った同じ質問だった。

「shutterは変わった?」

返事は十一回、同じversion 42。

そのうち一回だけが、新しいversion 43だった。

イトは500 ms timerを見た。

「順番は守れた。でも、変わったかどうかをずっと聞かないといけない」

live labでは、server側のstateが変わってから150 ms以内にviewerへ見せる必要がある。

空振りの確認は、六秒間に20回以下。

次のsceneが始まるまで、あと六秒だった。

500 msごとでは、変化の直後を逃した

ユイは同じ六秒のtraceを再生した。

networkのone-way delayは20 msに固定する。

serverのstateは、+2,051 msにversion 42から43へ変わった。

poll interval             500 ms
poll requests                  12
no-new-version replies         11
state changed          +2,051 ms
next request sent      +2,500 ms
new state visible      +2,540 ms
visible delay              489 ms

+2,000 msに出したrequestの返事は、+2,040 msに戻っていた。

その11 msあとにstateが変わる。

次のrequestまで、viewerには知る方法がなかった。

イト

通信が遅いんじゃない。次に聞くまで待っていたんだ

ユイ

pollingの間隔が、見えるまでの待ち時間に入っています

マコト

では、間隔を短くすれば終わりかな

イトはtimerを500 msから50 msへ回した。

六秒のあいだに、requestは120回。

変化を運んだ返事は一回だけだった。

poll interval              50 ms
poll requests                 120
no-new-version replies        119
new state visible      +2,090 ms
visible delay               39 ms

150 msの締切には間に合った。

けれど、空振り20回以下という条件を大きく越えた。

速く聞くほど、何も変わっていない返事も増える。

「もっと短く」は、二つの条件を同時には満たさなかった。

三つの連絡方法

イトはtimerの前へ三枚の案を並べた。

A:500 ms pollingを続ける

requestは12回で少ない。

しかし今回のvisible delayは489 ms。150 ms以内を満たさない。

B:50 ms pollingへ縮める

visible delayは39 ms。

しかしrequest 120回、no-new-version 119回になった。

C:最初にWebSocket connectionを一つ開き、変化時にserverから送る

opening handshakeが終わったあとは、clientだけでなくserver側も必要なときにapplication messageを送れる。

変化がない時間に「変わった?」を繰り返さなくてよい。

ただし、切れたconnectionが自動で元どおりになるわけではない。

再接続とstateの取り直しまで、同じ案に含める必要がある。

ユイ

どれを使うかは、欲しい速さだけでなく、変化の頻度でも変わります

マコト

HTTPでも下のconnectionを再利用できる。ここで比べるのは、毎回TCPを張るかではなく、applicationが尋ね続ける形だ

イト

じゃあC。聞く回数を増やさず、変わった側から送れる道を作る

Picoは案を選ばなかった。

timerにも、serverのsend switchにも触れない。

ただ、閉じた票の山と、まだ何も流れていない一本のrailをcyan lightで照らした。

イトが一本のconnectionをOPENにした

イトは50 ms timerを止めた。

viewerとserverのあいだに、secureなwss connectionを一つ開く。

opening handshakeが完了すると、connection stateはOPENになった。

CONNECTING
    ↓ opening handshake complete
OPEN

これは、HTTPが使えないという意味ではない。

WebSocketのopening handshakeもHTTPから始まる。

違うのは、そのあと一件ごとのpollingへ戻らず、同じconnection上で双方がmessageを送れることだった。

イトはserver側へ一つだけ条件を置いた。

when state.version changes
  send current absolute state

serverのstateが+2,051 msにversion 43 CLOSEDへ変わる。

serverは次のpollを待たず、その場でmessageを送った。

+2,071 ms、viewerのstate lockがamberに変わった。

opening handshakes before run      1
poll requests during run           0
no-new-version replies             0
state messages                     1
visible delay                  20 ms

イト

扉を開けたから速いんじゃない。変化した側が、次の質問を待たずに送ったんだ

ユイ

同じconnectionでviewerからもserverからもmessageを送れます

マコト

WebSocketはrealtimeという目的に使える一つの道具で、どの通信にも自動で最適とは限らない

イトが空振りの確認票を送るtimerを止め、viewerとserverの間の一本の双方向channelを開いたまま保って新しいstate capsuleを受け取る
質問の間隔を縮め続けず、一本のconnectionを保ち、変化した側から送る。

wssは、誰に送ってよいかまで決めない

イトが開いたのはwssのconnectionだった。

通信路はTLSで保護される。

けれど、鍵の付いた道があるだけで、誰に何を見せてよいかは決まらない。

誰がつないだか。

どのroomへ参加してよいか。

どのmessageを送ってよいか。

認証と権限は、application側で別に確かめる。

今回は一人のtest viewerと一つのshutterだけに閉じた。

chat roomの振り分けは、次のfixtureへ残す。

開いた扉も、切れる

最初の結果を見て、イトはOPEN lampへ完了札を掛けようとした。

その瞬間、labがnetworkを180 msだけ切った。

+3,500 ms、cyan railが暗くなる。

+3,550 ms、切れているあいだにserver stateはversion 44 OPENへ変わった。

viewerはそのmessageを受け取っていない。

+3,680 ms、clientがabnormal closeを観測した。

OPENだったという過去の表示は、いまつながっている証拠にはならなかった。

イト

もう一度OPENにすれば、届かなかった44も勝手に来る?

ユイ

connectionを戻すことと、離れていた間のstateを戻すことは別です

マコト

しかも全clientが切断直後から連打すると、serverへ再接続が集中する

イトは即時連打のreconnect switchを切った。

このfixtureでは250 ms待ち、一回だけ再接続する。

この250 msは一般解ではない。

実運用では、失敗が続くほど待ちを延ばし、開始時刻もずらすなど、同時再接続を避ける方針が要る。

+3,930 ms、イトがreconnectを一回開始。

+3,970 ms、opening handshakeが完了し、connectionは再びOPENになった。

イトはそこで終わらせなかった。

viewerが最後に受け取ったaccepted version 43を、自分の手でserverへ送る。

serverはcurrent snapshotのversion 44 OPENを返した。

+4,010 ms、viewerとserverのstateがそろった。

reconnect attempts              1
client before resync           43 CLOSED
server current                 44 OPEN
client after resync            44 OPEN
final state mismatch                0
ready before +4,200 ms             yes

WebSocketが直したもの、直していないもの

イトは結果を二列に分けた。

WebSocketが開いたのは、双方が必要なときにmessageを送れるconnectionだった。

変化がないのにrequestを繰り返す必要はなくなった。

一方で、次のことは自動では決まらない。

変化をすぐ受け取りたいなら、polling intervalを縮め続ける前に、変化したserver側から送れるconnectionが合うかを比べる。

WebSocketを選んでも、切断後の再接続とstateの取り直しは別に設計する。

接続が戻ってもclient / serverのversionがそろわないなら、application-level resyncまで確認する。

OPENは、applicationの完成を意味しない。

通信できる状態が一つ増えただけだった。

一本のrailへ、宛先の違う札が来た

+4,200 ms。

next live sceneには間に合った。

poll request 0。

no-new-version reply 0。

最初のstate changeは20 msで表示。

切断後も、version 43からcurrent 44を取り直した。

イトはtimerを机の端へ移し、reconnectとresyncの二枚の札をconnectionの横へ残した。

そのとき、開いたrailへ三枚のmessage cardが同時に滑り込んだ。

一枚はイトだけへ。

一枚は四人のroomへ。

もう一枚には、本文とは別の小さなstatus lampがついている。

イトは三枚を受け止めたが、どの箱へ入れるかはまだ決められなかった。

イト

道は開いた。でも、誰に届けて、どこまで届いた印を付ける?

ピコ

次は、connectionの先にあるchatの配達路を見よう

Picoはmessage cardを運ばない。

宛先の違う三枚と、イトの空いた三つのtrayを照らした。

次回、ピコルート第81話。

チャットの仕組みはどこを通る?

この物語を、解説で深める

第80話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

WebSocketとは|つないだまま双方向に送れるWeb通信を読む