RedisとCeleryを用いたバックグラウンド処理を徹底解説: 基本知識から実装まで
はじめに
私は現在,大学 4 年生で Web 開発の長期インターンに参加しています.API を用いてデータベースに保存する処理は長時間かかるため,バックグラウンド処理が必要になります.
かなり実装に手間取ってしまったため,同じようなバックグラウンド処理を行いたい人の参考になれば幸いです.
対象読者
- バックエンドエンジニア
- バックグラウンド処理とは何かを知りたい人
バックグラウンド処理とは
バックグラウンド処理とはユーザの操作(フロントエンドや API リクエスト)とは切り離されており,非同期で実行することができるので時間のかかる処理や即時応答が必要なタスクを効率的に処理するために用いられる.
今回実装した概要
API を使ってデータを取得し,集計後にデータベースに保存する仕組みを実装した.API から取得するデータは 1 万以上になることもあり,フロントエンドからのリクエストを受けて処理を開始し,途中結果を逐一返す必要がある(Heroku にデプロイしたのですが,30 秒以内に応答しなければいけないっぽい)
実装の詳細
バックグラウンド処理では,長時間のかかる処理の進行状況を管理するために,「Redis」 がよく用いられる.
Redis とは
Redis とはデータをハードディスクや SSD などのディスクではなく,メモリ上に保存するインメモリ型データストア(インメモリ型データストアとは RAM(メモリ)上にデータを保存することで,データの読み書きを高速化したデータストア)(データストとはキーと値のペアでデータを保存するデータベースの一種)
データの読み書きが非常に高速で,リアルタイム処理やキャッシュ,セッションの管理に適している.
Redis を用いたバックグラウンド処理の管理
例えば,API を 1 万回リクエストを送る処理があった場合,この処理はフロントエンドからのリクエストを受けてバックグラウンド処理を開始する.
処理の進行状況の管理は以下のように行われる.
- 進行状況の保存
Redis をキーと値の形式で利用し,以下の情報を保存
- 進行状況のステータス(ex. PENDING, IN_PROGRESS, COMPLETED)
- 処理の完了割合(1 回/100 回 = 1%)
- フロントエンドへの進行状況の通知
フロントエンドからの進行状況を確認するリクエストが送られてきた際に,Redis に保存されている(進行状況のステータス,処理の完了割合)のペアで値を返す.
ex. {"status": "IN_PROGRESS", "progress": "1%"}
Python を用いた実装
Celery を用いて Redis を扱っていく.
Celery とはバックグラウンドタスク処理をサポートする Python ライブラリで,タスクの非同期実行やスケジューリングに優れている.
Celery を使用する理由は FastAPI を用いて開発しているため.
Celery の実装ファイルは以下のようになります.
from celery import Celery
celery_app = Celery('analytics_app', include=['tasks.api_to_db', 'tasks.get_from_llm'])
redis_url = os.environ.get('REDIS_TLS_URL', 'redis://localhost:6379')
url = urlparse(redis_url)
celery_app.conf.update(
# ブローカーの設定
broker_url=f"{broker_url}/0",
broker_use_ssl=broker_ssl_options,
# バックエンドの設定
result_backend=f"{broker_url}/1",
redis_backend_use_ssl=broker_ssl_options,
# タイムアウトと再試行の設定
broker_connection_retry=True,
broker_connection_retry_on_startup=True,
broker_connection_max_retries=5,
broker_connection_timeout=30,
# タスク設定
task_serializer='json',
accept_content=['json'],
result_serializer='json',
# ワーカー設定
worker_max_tasks_per_child=1000,
worker_prefetch_multiplier=1,
result_expires=3600,
)
コードの詳細を解説
環境変数に Redis の URL が指定されている場合,それを用いるが,開発環境においては localhost になるようにしている
redis_url = os.environ.get('REDIS_TLS_URL', 'redis://localhost:6379')
ブローカ:タスクをどの順番で処理されるかを制御
バックエンド:タスクの進行状況を保存する場所
URL の後の"0"とは Redis のデータベース番号を指す.
Redis には通常,15-16 個のデータベースがあり,それぞれ独立したデータを保存する.
# ブローカーの設定
broker_url=f"{broker_url}/0",
broker_use_ssl=broker_ssl_options,
コードの残りの設定箇所は基本的なものであり,必要に応じて修正する必要あり
結論
Redis とはキーと値を保存できるメモリ上のデータベースであり,Celery はバックグラウンド処理を行うための Python のライブラリであり,Redis を扱うことが可能.
Discussion