文章

Django - 異步處理(Asynchronous Processing)

在現代 Web 開發中,異步處理(Asynchronous Processing)變得越來越重要,特別是當應用需要高效處理大量請求或執行 I/O 密集型操作時。從 Django 3.1 開始,Django 開始支持異步視圖與中介軟體,使其能更好地處理異步操作。


課程目標

  1. 理解同步與異步的區別。
  2. 瞭解 Django 的異步支持,包括異步視圖和中介軟體。
  3. 學習如何實現異步任務,並與外部服務進行協作。

課程內容

1. 同步與異步的基礎知識

1.1 同步 (Synchronous)

  • 同步請求處理是逐步執行的。
  • 當執行一個阻塞操作(例如 I/O 操作)時,程序會停下來等待結果。

1.2 異步 (Asynchronous)

  • 異步請求允許在執行阻塞操作時繼續處理其他請求。
  • 提升性能,特別是在處理大量 I/O 操作時(例如 API 調用、讀寫文件等)。

2. Django 的異步支持

2.1 異步視圖

從 Django 3.1 開始,視圖可以聲明為異步函數:

1
2
3
4
5
6
from django.http import JsonResponse
import asyncio

async def async_view(request):
    await asyncio.sleep(2)  # 模擬耗時操作
    return JsonResponse({"message": "This is an async view!"})

2.2 異步中介軟體

中介軟體也可以支持異步執行:

1
2
3
4
5
6
from django.utils.deprecation import MiddlewareMixin

class AsyncMiddleware(MiddlewareMixin):
    async def __call__(self, request):
        response = await super().__call__(request)
        return response

3. 異步任務實現

3.1 使用異步 HTTP 請求

在異步視圖中,可以使用第三方庫如 httpx 發起非阻塞的 HTTP 請求:

1
2
3
4
5
6
7
import httpx
from django.http import JsonResponse

async def fetch_data_view(request):
    async with httpx.AsyncClient() as client:
        response = await client.get('https://jsonplaceholder.typicode.com/posts/1')
    return JsonResponse(response.json())

3.2 與資料庫交互

Django 內建的 ORM 目前不支持異步操作,因此在異步視圖中仍然需要通過同步方式與資料庫交互。可以考慮將 ORM 操作移至線程池中執行:

1
2
3
4
5
6
7
8
9
from django.db import models
from asgiref.sync import sync_to_async

class Task(models.Model):
    title = models.CharField(max_length=255)

async def async_db_view(request):
    tasks = await sync_to_async(list)(Task.objects.all())
    return JsonResponse({"tasks": [task.title for task in tasks]})

4. 實現異步任務隊列

使用 Celery 配合 Django,實現異步背景任務執行。

4.1 安裝 Celery

1
pip install celery[redis]

4.2 配置 Celery

在項目根目錄下創建 celery.py

1
2
3
4
5
6
7
8
9
10
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')

app.config_from_object('django.conf:settings', namespace='CELERY')

app.autodiscover_tasks()

__init__.py 中導入 Celery:

1
2
3
4
from __future__ import absolute_import, unicode_literals
# 這確保應用程序啟動時載入 Celery
from .celery import app as celery_app
__all__ = ('celery_app',)

4.3 定義異步任務

tasks/tasks.py 中:

1
2
3
4
5
from celery import shared_task

@shared_task
def add(x, y):
    return x + y

4.4 調用異步任務

在視圖中調用 Celery 任務:

1
2
3
4
5
6
from django.http import JsonResponse
from .tasks import add

def trigger_task_view(request):
    task = add.delay(3, 4)  # 執行任務
    return JsonResponse({"task_id": task.id, "status": "Task triggered!"})

5. 測試異步功能

5.1 測試異步視圖

使用 Django 的測試框架測試異步視圖:

1
2
3
4
5
6
7
8
9
from django.test import AsyncClient
import pytest

@pytest.mark.asyncio
async def test_async_view():
    client = AsyncClient()
    response = await client.get('/async/')
    assert response.status_code == 200
    assert response.json() == {"message": "This is an async view!"}

5.2 測試 Celery 任務

測試 Celery 任務可以使用 apply 直接執行:

1
2
3
4
5
from .tasks import add

def test_add_task():
    result = add.apply((3, 4))  # 執行同步任務
    assert result.result == 7

總結

  1. Django 的異步支持允許開發者編寫高性能的 Web 應用。
  2. Celery 是執行背景任務的強大工具,與 Django 緊密集成。
  3. 異步功能適合 I/O 密集型操作,但需要注意 ORM 的同步限制。

作業

  1. 使用 Django 和 Celery 實現一個定時任務,模擬每日數據統計。
  2. 編寫一個異步視圖,調用多個外部 API 並整合結果返回。
  3. 嘗試將 ORM 操作移至線程池中,實現與資料庫的非阻塞交互。

本文章以 CC BY 4.0 授權