🍣
重たい処理はQueueへ!Azure FunctionsでAPIを軽量化する方法
はじめに
REST APIで受け付けた処理が重くてタイムアウトしがち...
そんな悩みを解決するために、Azure FunctionsのQueueTriggerを活用して「APIとバッチ処理を疎結合化」し、非同期にスケールする構成を組んだときの設計と実装を紹介します。
なぜQueueTrigger?
- REST APIに時間のかかる処理を入れると、ユーザー体験が悪くなりやすい
- 処理の失敗・リトライ管理も大変
- Queueをはさむことで、APIは即レス → 重処理は非同期という分離が可能
- Functions + QueueTrigger なら Azure標準でスケーラブルなバッチ処理を構築できる
システム構成
- API層は即レス(200 OK or 202 Accepted)
- 重たい処理(例:PDF生成、DB集計など)はQueueTrigger関数で非同期に処理
- Queueがバッファになってスパイクにも耐えやすい
実装概要
Queueに送信するメッセージ形式(例)
{
"userId": "abc123",
"operation": "generateReport",
"parameters": {
"year": 2024,
"type": "summary"
}
}
HTTP Trigger関数(API)
[FunctionName("RequestHandler")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
[Queue("report-requests", Connection = "AzureWebJobsStorage")] IAsyncCollector<string> queueCollector)
{
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
await queueCollector.AddAsync(requestBody);
return new AcceptedResult(); // 202 Accepted
}
QueueTrigger関数(バッチ処理)
[FunctionName("ReportGenerator")]
public async Task Run(
[QueueTrigger("report-requests", Connection = "AzureWebJobsStorage")] string message,
ILogger log)
{
var request = JsonConvert.DeserializeObject<ReportRequest>(message);
log.LogInformation($"Processing request for {request.UserId}");
// 実処理:DBからデータ取得 → PDF生成 → ストレージ保存など
}
実装時のTips
1. Queueのリトライ戦略とPoison Queue
-
処理中に例外を出すと最大5回リトライ(既定)
-
失敗が続くと自動的に<queue-name>-poisonに入る
-
Poison Queueを監視して、通知や補正処理を導入すべし
2. メッセージサイズの制限(64KB)
-
Azure Storage Queueは 64KB制限あり
-
超える場合はBlobに実体を保存 → メッセージにBlob URLを載せる方式に
3. 並列実行数の制御
-
host.json の batchSize, newBatchThreshold, maxConcurrentCalls で調整可能
-
ストレージやDBへの負荷に応じて適切に設定
まとめ
Functions + QueueTrigger を活用することで…
-
REST APIの応答速度を高速化(非同期化)
-
重処理のスケーラビリティと信頼性を向上
-
アーキテクチャ上も疎結合で保守しやすい構成に
単なる小さな関数ではなく、Azure Functionsは非同期バッチアーキテクチャの強力な選択肢になり得ることを実感しました。
Discussion