TT Lab
はじめる
学ぶ 学習パス コース

Tomcat & nginxの運用

スレッドプール・ヒープ・GCログを根拠を持って設定する

TT Labで続きを見る

目標

共有Executor・acceptCount・Keep-Alive・ヒープ・GCログを自分で設定し、リトルの法則でmaxThreadsを計算して、根拠の文書を残せるようになります。

なぜ重要なのか

性能の課題が出ると、いつも「maxThreadsを上げよう」という言葉が出ます。ところが、その値がどの地点で動作するかを知らないと、ほとんどの場合、何も変わらないか、さらに悪化します。maxThreadsは「同時に処理中のリクエスト」、maxConnectionsは「保持しているソケット」、acceptCountは「それさえ超えたときのOSのバックログ」で、バックログまであふれると、クライアントは503ではなくConnection refusedを受け取ります。この違いを知っている人が、障害を素早く終わらせます。

ステップ

  1. Tomcatを起動したあと、server.xmlの<Service>の中に共有Executorを追加します。name="tomcatThreadPool"、namePrefix="labhub-exec-"、maxThreads="150"、minSpareThreads="20"です。
  2. ポート8080のコネクターがこのExecutorを使うように、executor="tomcatThreadPool"属性を指定して再起動します。リクエストを何回か送ったあと、スレッドダンプを取って、/root/tc/threads.txtに保存してください。ダンプにlabhub-exec-のスレッドが見える必要があります。
  3. ポート8080のコネクターに、acceptCount="50"、maxConnections="2000"、connectionTimeout="20000"を設定します。
  4. ポート8080のコネクターに、keepAliveTimeout="5000"、maxKeepAliveRequests="100"を設定して再起動したあと、1回のcurlで同じコネクションに2つのリクエストを送り、その詳細出力(-v)を/root/tc/keepalive.txtに保存します。ファイルにRe-using existing connectionまたはConnection #0 to hostという、再利用の痕跡がある必要があります。
  5. /opt/tomcat/bin/setenv.shのCATALINA_OPTSに-Xms512m -Xmx512mを入れて、再起動します。実行中のプロセスの引数に、両方の値が見える必要があります。
  6. 同じCATALINA_OPTSにGCログのオプションを追加して、再起動します。ログファイルは/root/tc/gc.log、ファイルローテーションは5個・各10MBに設定します。再起動後、/root/tc/gc.logが生成されていて、空でない必要があります。
  7. /root/tc/load.shを作成します。引数を2つ(URL 동시요청수、韓国語の語は「同時リクエスト数」を意味します)受け取って、同時にリクエストを送り、最後の行にok=<성공수> fail=<실패수>を出力します(韓国語の語は、順に「成功数」「失敗数」を意味します)。HTTP 200なら成功、それ以外(404を含む)は失敗として数えます。
  8. /root/tc/sizing.mdを作成します。次のシナリオでリトルの法則を適用して、maxThreadsの値を計算し、計算過程と結果を残します。
    • 目標スループット: 300 TPS
    • 平均応答時間: 200ms
    • ピークの余裕率: 30% 文書には、maxThreads=<계산값>の形の行(韓国語の語は「計算値」を意味します)がちょうどある必要があり、TPS、ms、여유율(韓国語の語は「余裕率」を意味します)の3つの単語が、すべて登場する必要があります。

参考

共有Executorの定義

Tomcatを起動したあと、server.xmlの<Service>の中に共有Executorを追加します。name="tomcatThreadPool"、namePrefix="labhub-exec-"、maxThreads="150"、minSpareThreads="20"です。

ExecutorはServiceの子で、Connectorより先に宣言されている必要があります。namePrefixに意味のある名前を付けると、あとでスレッドダンプですぐに区別できます。

コネクターをExecutorに接続する

ポート8080のコネクターがこのExecutorを使うように、executor="tomcatThreadPool"属性を指定して再起動します。リクエストを何回か送ったあと、スレッドダンプを取って、/root/tc/threads.txtに保存してください。ダンプにlabhub-exec-のスレッドが見える必要があります。

コネクターにexecutor属性を指定すると、そのコネクターのmaxThreadsは無視されます。実際に適用されたかは、スレッドダンプでスレッド名を見れば確認できます。jcmdやjstackを使ってください。

コネクション受け入れのパラメーター

ポート8080のコネクターに、acceptCount="50"、maxConnections="2000"、connectionTimeout="20000"を設定します。

acceptCountは、maxConnectionsが尽きたあとのOSのバックログです。ここまであふれると、クライアントは503ではなく、接続拒否を受け取ります。

Keep-Aliveの設定と確認

ポート8080のコネクターに、keepAliveTimeout="5000"、maxKeepAliveRequests="100"を設定して再起動したあと、1回のcurlで同じコネクションに2つのリクエストを送り、その詳細出力(-v)を/root/tc/keepalive.txtに保存します。ファイルにRe-using existing connectionまたはConnection #0 to hostという、再利用の痕跡がある必要があります。

curl -vで、レスポンスヘッダーのConnectionの値と、コネクションの再利用の有無を確認できます。2つのリクエストを1回のcurlで送ると、再利用が観察されます。

ヒープの固定

/opt/tomcat/bin/setenv.shのCATALINA_OPTSに-Xms512m -Xmx512mを入れて、再起動します。実行中のプロセスの引数に、両方の値が見える必要があります。

サーバーアプリケーションは、最小ヒープと最大ヒープを同じにするのが慣行です。ヒープが伸びたり縮んだりして発生するGCをなくすためです。

GCログの有効化

同じCATALINA_OPTSにGCログのオプションを追加して、再起動します。ログファイルは/root/tc/gc.log、ファイルローテーションは5個・各10MBに設定します。再起動後、/root/tc/gc.logが生成されていて、空でない必要があります。

JDK 9以降は、-Xlogの統一ロギングを使います。ファイルローテーションのオプション(個数とサイズ)を一緒に指定しないと、GCログがディスクを埋めます。

同時負荷のスクリプト

/root/tc/load.shを作成します。引数を2つ(URL 동시요청수、韓国語の語は「同時リクエスト数」を意味します)受け取って、同時にリクエストを送り、最後の行にok=<성공수> fail=<실패수>を出力します(韓国語の語は、順に「成功数」「失敗数」を意味します)。HTTP 200なら成功、それ以外(404を含む)は失敗として数えます。

バックグラウンドで複数のcurlを起動して、waitでまとめる方式で十分です。各リクエストの成否は、HTTPステータスコードで判定してください。

スレッドプールの算出根拠の作成

/root/tc/sizing.mdを作成します。次のシナリオでリトルの法則を適用して、maxThreadsの値を計算し、計算過程と結果を残します。

リトルの法則: 同時処理数=目標TPS×平均応答時間(秒)です。余裕率を掛けたあと、切り上げます。計算過程を文書に残しておけば、6か月後に誰かがこの値を変えるとき、根拠になります。