理系学生日記

おまえはいつまで学生気分なのか

スレッド間うんたらでぼくが不幸になる

10,000 とか 100,000 件/分 を捌くようなオンラインシステムにおいては、待ち行列を作る部分がボトルネックになりがちです。
特に外部サービスと連携するような部分においては、そのネットワーク的な遅延や外部サービス側のレスポンス遅延が大きな影響を与えますが、これは本質的にどうしようもないことなども良くあります。外部サービスプロパイダに対して「もっと早くしろや」とも言いにくいわけで。

そうなると、個々の遅延を全体にできるだけ波及させないようにする必要があります。一つのリクエスト処理が遅れたとしても、後続のリクエストはそれに影響されないようにしなければなりません。Java でこのような実装を作るために頻繁に出てくるのが、外部サービスに 1 リクエストを投げ、そのレスポンスを受け取るという送受信の部分を子スレッドに実施させるという方法です。これにより、例えばパケットロスが発生し 1 要求のレスポンスタイムが大幅に遅れたとしても、他の子スレッドが行う送受信に対し、その遅延が影響を与えなくなります*1

ではこの子スレッドの生成であったり、その数の制御であったりを誰が行うのか。これについては、通常はジョブサーバだったり、あるいはパトローラと呼ばれる別スレッドだったりといった、オンラインサービスとは別のプロセス or スレッドが行います。オンラインシステム側は 10,000 件/分 を捌かなければならず、外部サービスのレスポンスを待つ暇はありません。従って、オンラインシステム側はジョブサーバに対し処理要求を発行するだけで直ぐに制御を再開し、ジョブサーバは上述の送受信スレッドを多数起動し、その制御を行うなんてアーキテクチャが生まれます。

外部サービスが複数になると、このようなアーテキテクチャが多数生まれます。さらにそれらの外部サービスを互いに連携させるような必要に迫られると、当然ながらスレッド間で、内部変数の情報を授受するためメソッドが生まれます。スレッドは本質的に互いに非同期に動作するため、それなりに考慮の上で、意図しないタイミングで内部変数が書き変えられたり、意図しないタイミングで意図しない値を取ったりするようなことを防がないといけません。この「それなりの考慮」がないと、人が不幸になります。生きる希望を失います。そしてぼくが不幸になった。

*1:もちろん、外部サービス側がそれだけの並行リクエストを捌けるという前提があってのことですが