こんにちは、プロダクトエンジニアリング部のちょうです。ずっと家にいまして時間の流れが気づきにくいと思いませんか。知らないうちに8月も何日しか残らないし、2021年も3分の2までが終わっています。家でテレワークしながら、何かおもしろいものないかなと考えたら、ふっと最近使っていたワクチン接種サイトを思い出しました。
ワクチン接種などのサイトは大量なユーザーから集中的なアクセスを予想できます。いくらサーバーを増やしてもさばけないと思われます。そしたら
- 接続数を制限
- アクセス時間帯を分ける
- キュー
などの施策が必要となります。アクセス時間帯を分けるのは利用するユーザーのIDなどをベースにアクセスできるタイミングを分散して集中アクセスを減らすアプローチです。そして減らした集中アクセスを予測してサーバーを増やすだけで対応できる可能性があります。すごくシンプルな考え方です。
そもそも負荷によって自動的にサーバーを増やして対応してもいいのではと思ってもかもしれませんが、ワクチン接種いわゆるチケット予約サイトはサーバーよりデータベースなどが先にボトルネックになってしまいます。複数枠、例えばワクチン接種予約の時間帯、を用意しても一つのレコードにロックをかけると、処理は一つずつになります(アクセスが集中するとき楽観ロックより悲観ロックがいいというデータがある)。案件によって、予約して裏側で非同期処理で結果を通知する仕組みもありますが、やはり時間内で結果を知りたいのが多いです。そうすると、リクエストをさばけるように事前に処理能力を計算し、サイトに入れるユーザーを制限するわけです。
制限といったら、API制限というのがあります。一般的にAPI制限はtoken bucketなどのアルゴリズムを利用し、一定時間内のアクセス数を制限します。これらのアルゴリズムは基本一台のサーバーなら簡単ですが、複数サーバーになると、Redisに計測値を共有して特殊のスクリプト(token bucketアルゴリズムにすくなくとも時刻と残るtoken数などのデータがある、Redisで安全に複数データを変更する方法がないため)で計算する方法があるらしいが、私が以前、1000回/60秒で2台でしたら500回/60秒を一台ずつ、サーバー数増減したら各サーバーの回数を自動的に調整できる仕組みを作っていました。ただ、API制限は基本チケット予約サイトに使えません。制限を超えたら、エラーになるだけです。ユーザーにとっては、「ただいまアクセスが集中しています」みたいの表示になり、リロードを試みるしかないです。
残りはキュー一択になります。自分の知る限り、ticket master、オリンピックチケット予約サイトなどに入ると、あなたがいま何番目のが表示され、自分の番になるまで待つだけでいいです。とても親切のように見えます。中身は何なのかはおそらく作った人しかわからないですが、今日は自分の理解でどうすれば同じ仕組みを作れるかを説明してみたいと思います。
処理能力内でしたらサイトに入る、処理能力を超えたら待つという仕組みは実際並列処理のSemaphoreととても似ています。Semaphoreに決まった数の許可(permit)があり、許可を取ろう(acquire)とするとき、まだ許可が残っているなら即時成功、残っていないなら待つ状態になります。一方、許可を取得したスレッドが許可を戻す(release)するとき、待っているスレッドがあればその許可をもらって処理を続けます。ここまで理解できるひとは「同時に取ろうとするスレッドがあれば誰がその許可をもらう?」という疑問を持っているかもしれません。ここで最初に待っているスレッドがもらうなら公平(fair)なSemaphoreといい、同時に取ろうとするスレッドが取れる場合は不公平(unfair)なSemaphoreといいです。現実でいうと先頭に割り込みとなります。ではSemaphoreとチケット予約サイトを比較してみましょう。
チケット予約サイト |
Semaphore |
予約者 |
スレッド |
サイトに入る |
許可を取る |
予約完了 |
許可を戻す |
待ち画面 |
スレッドが待つ |
予約画面に入る |
スレッドが起きる |
続きを読む