第17章:RedisベースのQueueを導入する(まずはここから)🟥📦
この章は「APIが仕事を投げる → Redisに溜まる → Workerが拾って処理する」という流れを、最小のコードで“動く”ところまで作ります💪✨ (Workerを別サービスにするのは次章でやるよ👷♂️➡️)
1) まず“キュー”を1枚絵で理解しよ🖼️🧠
イメージはこれ👇
[API] --(Jobを追加)--> [Redis(Queue)] --(Jobを取得)--> [Worker]
- API:HTTPリクエストにすぐ返したい(待たせない)🚀
- Redis:ジョブの“待合室”(順番待ちの箱)📦
- Worker:時間かかる処理を裏でやる係👷♂️
2) 今回使うライブラリ:BullMQ(Redisベースの定番)🐂⚙️
BullMQは TypeScriptで書かれていて、サンプルもTS中心なので教材と相性よし👍 (docs.bullmq.io) 内部のRedis接続は ioredis を使い、接続オプションもそれに沿う形です。(docs.bullmq.io)
3) Redis側を“キュー向け”にちょい設定🧯(超重要)
BullMQは Redisが勝手にキーを捨てる(eviction)設定だと正常に動けないので、maxmemory-policy は noeviction が推奨(というか必須級)です。(docs.bullmq.io)
compose.yaml の redis をこうしておくと安心👌
(すでにRedisがあるなら、command: 行だけ足してOKだよ)
services:
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
volumes:
- redis-data:/data
ports:
- "6379:6379"
volumes:
redis-data:
ついでに小ネタ:Composeのトップレベル
version:は今は“obsolete(形だけ)”扱いで、書かなくてOK寄りです。(Docker Documentation)
4) BullMQをインストールしよう📦✨
(npm例。pnpm/yarnでも同様!)
npm install bullmq
5) Redis接続情報を環境変数で統一🔑🧩
ここ、初心者が一番ハマりやすいポイント😇
- コンテナの中からRedisに繋ぐ →
localhostじゃなくて サービス名redisを使う - ホストPC(Windows)からRedisに繋ぐ →
localhost:6379
.env にこんな感じで置くのがラク👇
REDIS_HOST=redis
REDIS_PORT=6379
(次章でWorkerを別サービスにしても、同じ設定がそのまま使えます👍)
6) キューの“共通部品”を作る🧱🛠️
src/queue.ts(ファイル名はお好みでOK)
import { Queue } from "bullmq";
const QUEUE_NAME = "demo";
export const connection = {
host: process.env.REDIS_HOST ?? "redis",
port: Number(process.env.REDIS_PORT ?? "6379"),
};
export const demoQueue = new Queue(QUEUE_NAME, { connection });
export type DemoJob = {
message: string;
requestedAt: number;
};
ポイント👇
QUEUE_NAMEはAPIとWorkerで一致させる(ここが同じ箱📦)connectionは BullMQ → ioredis に渡る設定(host/portなど)(docs.bullmq.io)
7) APIからJobを投げる(enqueue)📨➡️📦
あなたのAPIが何製でもやることは同じ!
- リクエスト受ける
queue.add()する- すぐレスポンス返す(これが気持ちいい😆)
例(Expressっぽい最小イメージ)👇
import { demoQueue, type DemoJob } from "./queue";
export async function enqueueDemo(message: string) {
const payload: DemoJob = { message, requestedAt: Date.now() };
const job = await demoQueue.add("demo:print", payload);
return { id: job.id };
}
add("ジョブ名", データ)で投げる感じ。(docs.bullmq.io)
8) Workerを作る(処理する側)👷♂️🔥
src/worker.ts を作成👇
import { Worker } from "bullmq";
import { connection, type DemoJob } from "./queue";
const QUEUE_NAME = "demo";
const worker = new Worker<DemoJob>(
QUEUE_NAME,
async (job) => {
// ここが「重い処理」ゾーン💪(例なのでログ出すだけ)
console.log("✅ start:", job.name, job.id, job.data);
// 重い処理っぽく待つ(デモ)
await new Promise((r) => setTimeout(r, 800));
console.log("🎉 done:", job.id);
return { ok: true };
},
{ connection }
);
worker.on("failed", (job, err) => {
console.log("💥 failed:", job?.id, err.message);
});
console.log("👂 worker is listening...");
Workerは「ジョブを処理して、成功ならcompleted、失敗ならfailed」みたいに状態遷移します。(docs.bullmq.io)
9) “まずは同じコンテナ”でWorkerを起動してみよう🧪✨
次章で分離するから、この章は 同居運用でOK🙆♂️
- いつも通り起動(すでに起動中ならスキップ)
docker compose up -d
- Workerだけ別プロセスで起動
(
apiがあなたのAPIサービス名だとして)
docker compose exec api node dist/worker.js
もしまだTSを直接動かす運用(tsx等)なら、あなたのやり方に合わせて👇
docker compose exec api npm run workerdocker compose exec api npx tsx src/worker.ts
(この章の主役は“流れが動く”ことなので、起動手段は合わせてOK👍)
- APIにリクエストしてジョブ投入🎯 PowerShellならこんな感じ(例)👇
Invoke-RestMethod -Method Post http://localhost:3000/demo -ContentType "application/json" -Body '{"message":"hello queue"}'
- Worker側のターミナルに
✅ start ...→🎉 done ...が出たら勝ち🏆✨
10) よくある詰まり(ここだけ読めば大体助かる)🧯🪄
(A) ECONNREFUSED 127.0.0.1:6379 になる😇
→ コンテナ内からRedisに繋ぐときは localhost じゃなくて redis(サービス名)
.env の REDIS_HOST=redis を確認✅
(B) Eviction policy is ... should be 'noeviction' と怒られる😱
→ さっきの command: ["redis-server", ... "--maxmemory-policy", "noeviction"] を入れる
BullMQはここが超大事。(docs.bullmq.io)
(C) ジョブが溜まるだけで処理されない🥲 → Workerが起動してない! Queueは“箱”で、処理する人(Worker)がいないと永遠に待ちます📦🧍♂️
11) 設計のミニ定石(超入門だけど効く)🧠✨
- Queue名は用途で分ける(例:
email/image/report)📦📦📦 - Jobのデータは小さく(巨大な本文や画像そのものを入れない)🪶
- Jobは“同じのが2回実行されても壊れない”設計に寄せる(後でリトライするから)🔁 ※リトライ/失敗設計は次章・次々章でガッツリやるよ🔥
12) AI(Copilot / Codex)に頼むと速いところ🤖⚡
おすすめの頼み方(そのままコピペOK)👇
- 「BullMQで
QUEUE_NAME=demoのQueue/WorkerをTypeScriptで作って。Jobのpayload型も定義して。Redis接続はREDIS_HOST/REDIS_PORT。最小構成で。」 - 「
localhostを使わず、Docker Compose内接続はサービス名redis前提にして。」 - 「
maxmemory-policy noevictionのRedis設定も compose.yaml の差分で出して。」
AIの出力は便利だけど、**“hostがlocalhostになってないか”**だけ必ず目視チェックね👀(ここが事故りがち)
次章へのつながり🎁➡️
この章で「Queueの導入」と「API→Queue→Worker」が動いた!🎉 次章(第18章)で、いよいよ WorkerをComposeの別サービスとして分離して、 “開発スタック感”が一気に完成します👷♂️🚀