Celeryを活用した非同期タスク処理システムの実装パターン

システムアーキテクチャの設計

分散タスクキューを実装する際、主要コンポーネントとしてワーカープロセス、メッセージブローカー、タスク管理モジュールを設計する必要があります。Redisをメッセージブローカーおよび結果ストレージとして選定した理由は、軽量なインストール要件とCeleryとの高い互換性にあります。システム要件として、タスクのライフサイクル管理、エラーハンドリング、実行状況の可視化機能を組み込みます。

環境セットアップの最適化

依存関係は以下の構成で管理します:

celery==5.3.0
redis==4.6.0
flower==1.2.0  # モニタリング用

Redisサーバーの起動後、Celeryアプリケーションの初期化を次のように実装します:

from celery import Celery

task_app = Celery(
    'distributed_tasks',
    broker='redis://localhost:6379/0',
    backend='redis://localhost:6379/1'
)

task_app.conf.update(
    task_track_started=True,
    task_time_limit=300,
    task_retry_backoff=5,
    result_expires=3600
)

タスク処理の実装例

以下の3種類の非同期処理を実装します:

1. ユーザー通知処理

@task_app.task(bind=True, max_retries=3)
def send_notification(self, user_id, message):
    try:
        # 通知サービスの実装
        NotificationService.deliver(user_id, message)
        return {'status': 'success'}
    except ConnectionError as e:
        self.retry(exc=e, countdown=2 ** self.request.retries)

2. 画像変換処理

@task_app.task()
def resize_image(image_path, target_size=(800, 600)):
    with Image.open(image_path) as img:
        img.thumbnail(target_size)
        output_path = f"resized_{os.path.basename(image_path)}"
        img.save(output_path)
        return output_path

3. バッチ分析処理

@task_app.task()
def run_data_analysis(dataset_id):
    dataset = DatasetLoader.load(dataset_id)
    processor = AnalysisPipeline()
    processor.register_step(Normalizer())
    processor.register_step(StatisticalModel())
    return processor.execute(dataset)

実行状況の可視化

タスク進捗をリアルタイムで追跡するため、Redisに進捗状況を格納するメカニズムを実装します:

def update_progress(task_id, current, total):
    redis_client.setex(
        f"progress:{task_id}",
        3600,
        json.dumps({'current': current, 'total': total})
    )

# タスク内での使用例
update_progress(self.request.id, 5, 10)

Flowerを用いたモニタリングダッシュボードを構築することで、キューの状態、ワーカー負荷、タスク履歴を確認可能です。特に重要なのは、失敗タスクの自動リトライメカニズムと、各タスクタイプを専用キューに割り当てることによるリソース分離です。

テスト戦略の設計

単体テストではCeleryのテストモードを活用し、非同期処理を同期的に検証します:

def test_image_resize():
    with task_app.pool.acquire(block=True) as connection:
        result = resize_image("test.jpg")
        assert result.startswith("resized_test.jpg")
        assert os.path.exists(result)

タスクのべき等性を確保するため、各実行にUUIDを付与し、重複実行を防止する仕組みを組み込みます。また、タイムアウト設定と適切なリトライ戦略の組み合わせにより、ネットワーク不安定時の処理を堅牢にしています。

タグ: Celery redis asynchronous-processing task-queue Python

7月11日 16:06 投稿