夏休みの宿題/SO_REUSEPORTを使ってみた
昨日作った宿題のランダムな数字を返すデーモンですが、ちょっと困った問題があります。
それは、同時に複数のリクエストを処理できないこと。
もったいぶって待つ秒数が0.2秒なのでわかりにくいですが、例えば5クラアントが同時にアクセスすると、
最初に接続されたクライアントが0.2秒後にレスポンスを受け取り切断、
次のクライアントが接続し0.2秒後にレスポンスを受け取り切断、
…..
…..
5番目のクライアントが接続し0.2秒後にレスポンスを受け取り切断。
結果として1秒間にどう頑張っても5リクエストしか処理できないのです。
5並列で合計100リクエスト投げた場合にかかる時間はこんな感じ。
1 | [rhykw@builder11 ~]$ time ( n=0;while [[ $((n++)) -lt 100 ]];do echo 1;done|xargs -I% -n1 -P5 nc 127.0.0.1 8888 > log.txt) |
ほぼ計算上の数字通り、という感じです。
今回はサーバーがクライアントへランダムな数字を送りつけるだけなのであまり問題は無いですが
これがHTTPのようなプロトコルなら大問題。
誰かがアクセスしている間は他のクライアントは待たされる、では困ります。
で、教科書的な実装については68userさんのネットワークプログラミングの基礎知識といったサイトで言及されているように
selectなりforkなりを使って同時処理を受付出来るようにするのが良いと思います。
が、、、
今日はあえて以前 SO_REUSEADDR
を調べた際に気になっていた SO_REUSEPORT
を実際に使ってみます。
ざっっっっくり言うと SO_REUSEPORT
は、既に誰かがListenしているポートを別のプロセスもListenするときに使うフラグ。
最初にListenした誰かも SO_REUSEPORT
である必要がある。
変更後のソースは rndserver2.c
実質追加したのは下記の部分です。
1 |
|
併せて、ホントに複数のプロセスに処理が分散されているか確認しやすくするため、
ランダムな数字の前に、プロセスIDも出力します。
で、測定結果。
まずサーバー側を起動。xargsで10プロセス立ち上げ。
1 | [rhykw@builder11 tmp]$ n=0;while [[ $((n++)) -lt 10 ]];do echo 1;done|xargs -I% -n1 -P10 ./rndserver2 |
次にクライアント側。まず5並列。
1 | [rhykw@builder11 ~]$ time ( n=0;while [[ $((n++)) -lt 100 ]];do echo 1;done|xargs -I% -n1 -P5 nc 127.0.0.1 8888 > log.txt) |
出力も見てみます。
リクエスト数が少ないので少しばらつきがありますが分散されているのがわかります。
1 | [rhykw@builder11 ~]$ awk -F, '{print $1}' log.txt|sort|uniq -c |
今回は、雑な例を挙げちゃいましたが、うまく使えばマルチスレッド化されているデーモンでも
リロード/リスタートの際に新しいプロセスを追加起動し、古い方のデーモンはkillする、といった
使い方が出来るのかもしれません。
もう少し触ってみようと思います。