Table of Contents [expand]
最終更新日 2025年09月05日(金)
Web アプリケーションで、エンドユーザーのリクエストに可能な限り迅速に応えることは重要です。Web リクエストの実行時間が 500 ミリ秒を超えないようにする、というのが経験則です。完了に 1 秒、2 秒、あるいはそれ以上かかるリクエストがアプリに存在する場合、代わりにバックグラウンドジョブを使用することを検討してください。
このアーキテクチャパターンについての詳細は、Worker dyno、バックグラウンドジョブ、キューイング の記事を参照してください。
Node.js サーバーで、計算コストが高いリクエストによってイベントループがブロックされる可能性がある場合、この考慮事項はさらに重要です。このブロックによって、計算が完了するまでサーバーが新しいリクエストに応答できなります。この計算を別のプロセスに分離すれば、Web サーバーの軽量性と応答性が維持されます。
この記事では、ワーカーキューとサンプル Node.js アプリケーションで BullMQ を使用してバックグラウンドジョブのキューを管理する方法について説明します。

この記事では、Redis (ローカル開発用) と Heroku CLI がインストール済みであることを前提としています。
Fir 世代のアプリではこのチュートリアルを実施できません。
dyno を使用してこのチュートリアルを完了した場合、使用量のカウントに入ります。低料金プランを使用してこのチュートリアルを完了することをお勧めします。資格のある学生の皆様は、新しい Heroku for GitHub Students プログラムを通じてプラットフォームクレジットを申請できます。
はじめに
以下の手順を完了して、このアプリケーションを Heroku アカウントに複製します。
ダッシュボードから行う場合
(デプロイ) をクリックします。- ジョブが処理されるよう、ダッシュボードの
Resources(リソース) タブでworker プロセスを少なくとも 1 つの dyno にスケーリングします。 - ブラウザでアプリを開いて新しいジョブを開始し、完了するまで監視します。
CLI から行う場合
$ git clone git@github.com:heroku-examples/node-workers-example.git
$ cd node-workers-example
$ heroku create
$ heroku addons:create heroku-redis
$ heroku addons:wait
$ git push heroku main
$ heroku ps:scale worker=1
$ heroku open
アプリケーションの概要
アプリケーションは 2 つのプロセスで構成されます。
web— フロントエンドアセットを提供し、新しいバックグラウンドジョブを受け付けて、既存のジョブのステータスを報告する Express サーバー。worker— 受信ジョブを実行する小さな Node.js プロセス。
これらのプロセスは、特定のアプリケーションのニーズに基づいて個別にスケーリングできます。Heroku のプロセスモデルについての詳細は、「プロセスモデル」の記事を参照してください。
web プロセスは、新しいジョブを開始してそれらを監視する簡略化したフロントエンドインターフェースの例を実装する index.html および client.js ファイルを提供します。

Web プロセス
server.js はごく小さな Express サーバーです。注目すべき重要な点は、Redis サーバーへの接続と、名前付きワークキューの設定です。
// Connect to a local redis instance locally, and the Heroku-provided URL in production
const redisUrl = new URL(env.REDIS_URL || 'redis://127.0.0.1:6379')
const redisConnection = new Redis({
host: redisUrl.hostname,
port: redisUrl.port,
user: redisUrl.username,
password: redisUrl.password,
// Redis connections on Heroku use TLS to encrypt traffic + self-signed certificates so we'll
// configure this client with `rejectUnauthorized` to treat this connection as trusted.
// See: https://devcenter.heroku.com/articles/connecting-heroku-redis
tls: redisUrl.protocol === 'rediss:' ? { rejectUnauthorized: false } : false,
})
// Create / Connect to a named work queue
const workQueue = new Queue('work', {
connection: redisConnection
})
もう 1 つの重要な注意点は、POST リクエストを受信したときのジョブの開始です。
app.post('/job', async (req, res) => {
const data = {} // empty for this example app
const job = await workQueue.add('example', data)
console.log(`Enqueued job: ${job.id}`)
res.json({
id: job.id
})
});
通常、バックグラウンドジョブの開始への直接アクセスをこのようにクライアントに付与することはありませんが、この単純な例は説明を目的としたものです。
ワーカープロセス
worker.js は、pm2 を使用してワーカープロセスのクラスターを起動します。この例では、ジョブは解決の前に少しスリープしますが、これは独自のワーカーの作成を開始するのに良い機会です。
並列性に関する 2 つの概念を理解することが重要です。1 つ目は、Dyno でクラスターとして実行中のワーカーインスタンスの数で、pm2.config.cjs にあるアプリケーション構成で定義されます。
{
name: 'worker',
script: "./worker.js",
exec_mode: "cluster",
instances: env.WEB_CONCURRENCY || 1
}
各ワーカーインスタンスは、pm2 のクラスターモードで管理されている、独立したイベントループのあるスタンドアロンの Node.js プロセスです。Heroku dyno では、デフォルト値は WEB_CONCURRENCY 環境変数で設定されます。この値は dyno のメモリ量に応じて増減しますが、特定のアプリケーションに合わせたチューニングが必要な場合があります。詳細は、「Node.js アプリケーションの並列性の最適化」を参照してください。
並列性に関する 2 つ目の概念は、各ワーカーが一度に処理するジョブの最大数です。
new Worker('work', processJob, {
connection: redisConnection,
concurrency: 50
})
各ワーカーは Redis キューからジョブを選択して処理します。この設定は、各ワーカーが一度に処理を試みるジョブの数を制御します。
多くの場合、アプリケーションに合わせてこの設定をチューニングするタスクを完了する必要があります。各ジョブで主に待機するのが、外部の API やサービスのようなネットワーク応答である場合、この設定値を大きくします。各ジョブの CPU 使用率が高い場合は、この設定を 1 まで下げることを検討します。この場合、ワーカープロセスの起動数を増やしてみることをお勧めします。
クライアント Web アプリ
client.js では、ジョブを開始してその進捗状況を監視できるよう、ごく小さな Web フロントエンドを実装します。この記事で説明したのは、BullMQ の機能のほんの一例です。以下を含むさらに多くの機能が備わっています。
- キューの優先順位付け
- 速度制限
- ジョブのスケジューリング
- 再試行
これらの機能の使用方法についての詳細は、BullMQ のドキュメントを参照してください。