Node.js アプリケーションの並列性の最適化
最終更新日 2024年05月15日(水)
Table of Contents
Node の別のコンテナサイズへのスケーリング機能は限られています。シングルスレッドであるため、追加の CPU コアを自動的に利用することはできません。
Node.js アプリがその使用可能なリソースを最大化するには、複数のプロセスをフォークする必要があります。この “クラスタリング” は Node.js Cluster API でサポートされています。Cluster API をアプリで直接呼び出すか、またはこの API 経由で多数の抽象化のいずれかを使用することができます。ここでは、Throng を使用します。
Cluster API を使用すると、さまざまな dyno タイプにわたってアプリのパフォーマンスを最適化できます。Heroku Node.js buildpack には、役立つ環境変数が用意されています。
Premier または Signature Success Plan の Heroku Enterprise の顧客は、Customer Solutions Architecture (CSA) チームに、このトピックに関する詳細なガイダンスを要求できます。ここでエキスパートコーチングセッションについて学習するか、または Salesforce の担当者にお問い合わせください。
アプリでの並列性の有効化
すべてのアプリケーションでクラスタリングをサポートすることをお勧めします。現在は複数のプロセスの実行を予測していない場合でも、クラスタリングによって、アプリのパフォーマンスに対する制御と柔軟性が向上します。では例を見てみましょう。
最初に、クラスタリングするプロセスの数を決定します。
var WORKERS = process.env.WEB_CONCURRENCY || 1;
2 番目に、新しくクラスタリングされた各プロセスのエントリポイントとして start
関数を定義します。
function start() {
// ...
}
3 番目に、throng
を使用して、アプリを複数のプロセスにクラスタリングします。ワーカーが停止した場合は再生成するよう throng に指示するために、Infinity
の有効期間を指定します。これによって、WORKERS
プロセスは常に実行中の状態になります。
throng({
workers: WORKERS,
lifetime: Infinity
}, start);
ローカルでのテスト
並列性を実装したので、クラスターを観察できます。
$ npm start
> example-concurrency@1.0.0 start example-concurrency
> node server.js
Listening on 3000
$ WEB_CONCURRENCY=4
$ npm start
> example-concurrency@1.0.0 start example-concurrency
> node server.js
Listening on 3000
Listening on 3000
Listening on 3000
Listening on 3000
並列性レベルのチューニング
各アプリにはメモリ、CPU、I/O に関する固有の要件があるため、1 つのサイズですべてを満たすスケーリングソリューションはありません。Heroku buildpack には、WEB_MEMORY
と WEB_CONCURRENCY
の 2 つの環境変数による妥当なデフォルト値が用意されています。どちらも、特定のアプリケーションに適合するように上書きできます。
WEB_MEMORY
は、アプリケーションのプロセスの予測されるメモリ要件 (MB 単位) を指定します。そのデフォルト値は 512 MB です。WEB_CONCURRENCY
は、アプリケーションをクラスタリングするための推奨されるプロセス数を指定します。これは実質的にMEMORY_AVAILABLE / WEB_MEMORY
です。
クラスタリング時のアプリケーションのメモリ使用の設定に関する詳細を参照してください。
デフォルト値
Common Runtime
dyno タイプ | クラスターワーカーの数 |
---|---|
Eco, Basic, Standard-1X | 1 |
Standard-2X | 2 |
Performance-M | 5 |
Performance-L | 28 |
Performance-L-RAM | 15 |
Performance-XL | 31 |
Performance-2XL | 63 |
Private Spaces と Shield Private Spaces
dyno タイプ | クラスターワーカーの数 |
---|---|
Private-S / Shield-S | 2 |
Private-M / Shield-M | 5 |
Private-L / Shield-L | 28 |
Private-L-RAM / Shield-L-RAM | 15 |
Private-XL / Shield-XL | 31 |
Private-2XL / Shield-2XL | 63 |
Performance-L dyno の場合、アプリケーションは、その 14 GB のメモリに推奨される 28 個のワーカーで適切に動作します。常にアプリケーションをテストして、それだけの数の並列プロセスをサポートできるかどうかを確認してください。
これらのデフォルト値は、ほとんどのアプリにとって妥当です。多くの場合、Standard-1X dyno 上での複数のワーカーのクラスタリングはパフォーマンスを向上させるのではなく、むしろ低下させます。ただし、WEB_CONCURRENCY
と dyno サイズの任意の組み合わせを試してみて、実際のワークロードにとって何が最適であるかを確認することができます。
WEB_MEMORY
を減らすと、WEB_CONCURRENCY
が増加します。同様に、WEB_MEMORY
を増やすと並列性が低下します。dyno のサイズが変更されると、使用可能なメモリがいっぱいになるように WEB_CONCURRENCY
が自動的に再計算されます。
WEB_CONCURRENCY
を直接設定することもできますが、これにより、dyno サイズが変更されたときにアプリが自動的に再クラスタリングできなくなります。
アクションでの確認
起動時に Node の並列性設定をログに記録するには、LOG_CONCURRENCY
環境設定を設定します。
$ heroku config:set LOG_CONCURRENCY=true
クラスタリングが有効なアプリを Heroku にデプロイしたら、そのログを末尾監視して、さまざまなコンテナサイズへのスケーリングを観察できます。
$ heroku logs --tail
$ heroku scale web=1:standard-1x
heroku[api]: Resize web to standard-1x
heroku[api]: Scale to web=1
heroku[web.1]: State changed from up to starting
heroku[web.1]: State changed from up to starting
heroku[web.1]: Starting process with command `npm start`
app[web.1]: Detected 512 MB available memory, 512 MB limit per process (WEB_MEMORY)
app[web.1]: Recommending WEB_CONCURRENCY=1
heroku[web.1]: Stopping all processes with SIGTERM
app[web.1]:
app[web.1]: > example-concurrency@1.0.0 start /app
app[web.1]: > node server.js
app[web.1]:
app[web.1]: Listening on 51077
heroku[web.1]: State changed from starting to up
$ heroku scale web=1:performance-l
heroku[api]: Resize web to performance-l
heroku[api]: Scale to web=1
heroku[web.1]: State changed from up to starting
heroku[web.1]: Starting process with command `npm start`
app[web.1]: Recommending WEB_CONCURRENCY=12
app[web.1]: Detected 6144 MB available memory, 512 MB limit per process (WEB_MEMORY)
heroku[web.1]: Stopping all processes with SIGTERM
app[web.1]:
app[web.1]: > example-concurrency@1.0.0 start /app
app[web.1]: > node server.js
app[web.1]:
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
app[web.1]: Listening on 50092
heroku[web.1]: Process exited with status 143
heroku[web.1]: State changed from starting to up