エンジニアは比喩で世界を理解する生き物である。データベースで記憶を語り、キャッシュで習慣を語り、負債で組織の歪みを語る。であれば、人間のコミュニケーションを通信プロトコルで解剖してみるのもまた、エンジニアらしい営みといえるのではないか。
この記事は、自分自身の対人関係のパターンを振り返るうちに「これはプロトコルの問題だったのか」と腑に落ちた話を書く。技術的に厳密なアナロジーではないけれども、自身や周囲のコミュニケーションを理解するフレームワークとしてはかなり使える気がしている。
TCP — 私のデフォルトプロトコル
私のコミュニケーションは、基本的にTCPで動いている。
TCPは信頼性のあるプロトコルである。通信の前に三方向ハンドシェイクで接続を確立し、データが届いたらACKと呼ばれるデータ受領を伝える確認応答パケットで確認し、パケットの順序も保証する。確実だが、オーバーヘッドが大きい。
私は人と話すとき、常にACK、つまり相槌やリアクションのようなものを求めている。自分の発言が届いたか、相手がどう受け取ったか、接続は今も生きているか。沈黙が訪れると不安になる。パケットがドロップしたのではないか、接続が切れたのではないかと疑ってしまう。であるからあえて無駄口を叩く。ふざけて場を軽くする。これは言ってしまえば次に挙げるpingのようなものだ。
ICMP — 接続確認用の軽いプロトコル
ICMPとは、接続確認やエラー通知に特化した非常にシンプルで軽量なプロトコルである。ハンドシェイクや再送などの機能もなく、大した容量も送れない。
私が軽い冗談や無駄口を叩くのは、まさにこのプロトコルを使って接続を確認するだけの行為である。相手にpingを打って、pongが返ってくることで、まだ繋がっているな、と確認する。ただそれだけの行為と言える。
このコミュニケーションの問題は、pongが返ってきても安心が持続しないことだ。通信は一瞬で終わる。確認できたのはその瞬間だけの事実であって、次の瞬間にはもう不確定に戻っている。沈黙が訪れると、接続ができているのかどうか不安になる。通信相手とのつながりが本当にまだそこにあるのか、相手が何を考えているのかがわからず不安に襲われる。だからまたpingを打つ。永遠にステートレスな関係が続き、過去の通信が信頼として蓄積されない。
普通の人はある程度のやりとりを経ると、接続の信頼性をキャッシュできるらしい。この人とは大丈夫だ、という前提で動けるようである。しかし私はそのキャッシュが効かない。もしくは、この方式の通信におけるキャッシュの寿命、つまりはTTLが極端に短い。毎回ゼロから接続を確認しなければ気が済まない。
本来ステートフルであるべき接続を、ステートレスに運用しているのだから、疲れるのは当然なのかもしれない。
UDP — 届いたか確認しないという自由
UDPはTCPの対極にいる。接続の確立もACKの確認もしない。データを投げたら、届いたかどうかは気にしない。パケットが落ちても再送しない。
多くの人の「中間距離のコミュニケーション」はこれだと思う。同僚との雑談、すれ違いざまの挨拶、Slackのスタンプ。送りっぱなしで、たまに返ってくればそれでいい。パケットロスなんて誰も気にしていない。
私はUDPのようなコミュニケーションが得意でない。得意でないというのは嫌いだとかそういう意味ではなく文字通りの意味で、上手でない。しかしこういったコミュニケーションに憧れている。届いたか確認しない。相手の反応を追わない。投げっぱなしで平気。それは無責任なのではなく、「通信が途切れても自分が壊れない」という前提を持っている人にしかできないことだ。UDPで通信できるということは、接続の維持に自己の存続がかかっていないということであると私は思う。
私にはその割り切りがない。接続が切れることと、自分の居場所がなくなることが直結しているような感覚がある。だから仮にUDPで送信するふりをしていても、裏ではTCPのハンドシェイクを要求してしまう。言いたいことを一方的に言っているような無遠慮な通信の体裁をとっていたとしても、その実相手の反応を注視してしまっている。表面はUDP、中身はTCP。無意味なプロトコルの偽装をするケースもあるわけだ。
HTTP — 虚無のプロトコル
HTTPはリクエストとレスポンスの一問一答で成り立つプロトコルである。クライアントがリクエストを送り、サーバーがレスポンスを返す。それで終わり。接続は閉じられる。
ビジネスメール、事務連絡、問い合わせへの回答。用件があるときだけ通信して、用件が済んだら接続は消える。私はHTTPに最も虚しさを感じる。
接続の確立は一瞬で、通信内容は簡潔で、終わったら何も残らない。私にとってはキャッシュに書き込まれる余地がない。こちらがどれだけ丁寧にレスポンスを返しても、相手にとってはAPIを一回叩いただけのことだ。次のリクエストが来るかどうかもわからない。来なければ、それで関係は終わりだ。何の痕跡も残らない。
HTTPの世界では、通信の質は問われない。ステータスコード200が返ればそれでいい。私が返したレスポンスの内容がどれだけ練られていようと、相手が見ているのは「正常に処理されたかどうか」だけだ。
これが虚しいと感じること自体が、おそらく私の偏りなのだろう。世の中の大半のコミュニケーションはHTTPで十分に機能しているし、それで誰も困っていない。困っているのは、すべての通信に接続の実感を求めてしまう私のほうだ。
WebSocket — 常時接続という安心
WebSocketは、一度接続を確立したら双方向に常時開かれたままになるプロトコルである。いつでもデータを送れるし、いつでも受け取れる。接続の確認は不要。回線はずっとそこにある。
親友、恋人、家族の一部。ごく少数の、深い関係がこれにあたる。
WebSocketの関係においてだけ、私は安心できる。pingを打たなくても接続が確立されていることは保証されているから、沈黙がまったく怖くない。黙っていても接続は維持されているという確信がある。キャッシュがようやく永続化される領域であるといえる。
しかしやはり、WebSocketの接続を確立するまでのコストは高い。最初はHTTPでハンドシェイクして、プロトコルをアップグレードして、ようやく常時接続に至る。私の場合、そこに至るまでの閾値がおそらく人より高い。かなりの通信量と深度を経ないと、「この接続は信頼できる」というフラグが立たない。
当然、WebSocketの関係は数が少ない。それは意図的に絞っているわけではなく、単純にアップグレードの条件を満たす関係が限られていることに起因する。
マルチキャスト — 一方向だからこそ安全な通信
マルチキャストは、一つの送信元から複数の受信先に同時にデータを配信するプロトコルだ。テレビの放送、ライブ配信、そしてLTの発表やブログ。
社会に出てから気づいたのだが、私はマルチキャストが得意で、プレゼンのような場で普通の人が感じる緊張もあまり感じないらしい。私にとっては比較的気楽なプロトコルといえるかもしれない。
理由は明快で、双方向の確認が不要だからである。一対一のTCP通信では常にACKを追いかけてしまう私が、一対多の配信になると途端に楽になる。受信者の個別の反応を逐一確認する必要がないし、沈黙もあって当然であるという認識のもと成り立つ通信であるから、今更どうとも思わない。送信し続けている限り、通信は成立しているわけである。
LTで何か技術的な発表をするとき、ブログで思考を書き下すとき、私はTCPのオーバーヘッドから解放されている。伝えたいことを伝えたい形で送信して、それが届いたかどうかは受信者に委ねる。これは私が普段の一対一では決してできないUDP的な投げっぱなしに近い。
最近入社した会社で、すでに複数回LTを実施している。これはなぜかと考えると、マルチキャストは私にとって、TCPの苦しみを回避しながら人と繋がれる貴重な生存戦略だったのかもしれない。一対一の接続を確立せずに、不特定多数に自分を送信できる。仮面をかぶらなくていい。むしろ、マルチキャストしているときの自分が一番素に近いとさえ感じることもある。
MQTT — 生存確認という最小限のやさしさ
MQTTはIoTの世界でよく使われる軽量プロトコルである。常時接続ではあるが、通信量は極めて少ない。「生きているか」という最小限のシグナルを、定期的に、低帯域で送る。
誰もが一度はこんな経験をしたことがあると思う。不意に古い友人から「元気か」「生きてるか」と、それだけのメッセージを受け取る。何か重要な連絡でもないし、また今度飲もうだなんて言いながらも、内容はほぼない。しかし、そのメッセージを受け取ったこと自体に、なんだか安心するような感覚を持つ。
数年間の沈黙があったのに、接続が生きている。しかもそれは相手からの発信だから、自身がpingを打つ必要もない。向こうから来る。こういった通信は、最小限でありながら、十分で、意味があって、何にも代え難い価値のあるものであると私は感じる。
MQTTの関係では、私のキャッシュはちゃんと効いている。数年分の沈黙に耐えられている。これは重要な発見だった。私にキャッシュが一切効かないわけではない。十分に深い通信を過去に経た関係では、ちゃんとキャッシュが残り、むしろ他の人よりもずっと長期間有効であり続けるように思う。
ここでもやはり問題はキャッシュの書き込み閾値が高いということだ。UDPレベルのやりとりでは書き込まれない。WebSocketレベルの深い通信を経て、ようやく永続化される。一度永続化されれば、あとはMQTTの最小限のシグナルだけで維持できる。
不意に受信するMQTTのメッセージは、私にとってとてもありがたい。「お前との接続はまだ生きているぞ」という、最も低コストで、最も確実な存在証明、友情や愛情の表明ようなものである。
結論 — 自分のプロトコルスタックを知ること
ここまで書いて見えてきたのは、私の通信プロファイルだ。
- WebSocketで常時接続できる少数の関係が生命線
- MQTTの古い友人が、静かな安心の層として存在する
- マルチキャストは、TCPの負荷を回避しながら自分を表現できる安全な形式
- TCPの中間距離が最もコストが高く、ここで消耗する
- UDPで通信できる人たちに憧れるが、自分にそのプロトコルは実装されていない
- HTTPの一回限りのやりとりに、最も虚しさを覚える
これを知ったからといって、自分のプロトコルを書き換えられるわけではない。TCPで動いている自分を、明日からUDPにはできない。キャッシュのTTLを延長するコマンドもない。
しかし、なぜ自分は中間距離の関係で疲れるのか、なぜ一人のときは楽なのか、なぜLTやブログでは自然に振る舞えるのか。これらの問いに、プロトコルという言葉が一貫した説明を与えてくれた気がした。
自分のOSを書き換えることはできない。けれども、自分がどんなプロトコルスタックで動いているかを知ることはできる。相手に合わせたプロトコルを選ぶことが重要なことと同様に、自分自身が好み必要とするプロトコルが何なのかを把握することもまた重要なことであると気付かされた。そしてその理解が正確であれば、少なくとも理不尽な社会不適合感は減る。
バグを直せなくても、バグが起きる作用機序や再現手順さえわかっていれば、致命的なエラーは回避するよう運用でカバーできる。これはおそらく、そういったタイプの対処法を取るべき問題なんだと思う。やりにくさや気まずさ、コミュニケーションの難しさは日々感じるけれども、それは自分にも一因は当然あって、その傾向を知ることで大なり小なりやり方のようなものを見つけていけるのかもしれない。なかなか難しいけれど。