Перейти к содержанию

Background Tasks

Как и в Lilya, в Esmerald вы можете определять фоновые задачи для выполнения после возвращения ответа.

Это может быть полезно для тех операций, которые должны происходить после запроса, не блокируя клиента (клиенту не нужно ждать завершения) для получения того же ответа.

Примеры:

  • Регистрация пользователя в системе и отправка электронного письма с подтверждением регистрации.
  • Обработка файла, которая может занять "некоторое время". Просто верните HTTP 202 и обработайте файл в фоновом режиме.

Как использовать

Как уже упоминалось, Esmerald использует фоновые задачи из Lilya, и вы можете передавать их различными способами:

Через handlers

Это означает передачу фоновой задачи через get, put, post, delete или route.

Существует также два способа передачи через обработчики.

Использование одного экземпляра

Это, вероятно, наиболее распространенный случай, когда вам нужно выполнить одну фоновую задачу при получении запроса, например, отправка уведомления по электронной почте.

from pydantic import BaseModel

from esmerald import BackgroundTask, JSONResponse, post


class UserIn(BaseModel):
    email: str
    password: str


async def send_email_notification(message: str):
    """Sends an email notification"""
    send_notification(message)


@post(
    "/register",
    background=BackgroundTask(send_email_notification, message="Account created"),
)
async def create_user(data: UserIn) -> JSONResponse:
    JSONResponse({"message": "Created"})

Этот пример довольно простой, но он иллюстрирует, как вы могли бы использовать обработчики для передачи фоновой задачи.

Использование списка

Конечно, есть также ситуации, когда нужно выполнить более одной фоновой задачи.

from datetime import datetime

from pydantic import BaseModel

from esmerald import BackgroundTask, BackgroundTasks, JSONResponse, post


class UserIn(BaseModel):
    email: str
    password: str


async def send_email_notification(message: str):
    """Sends an email notification"""
    send_notification(message)


def write_in_file():
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now}"
        log.write(content)


@post(
    "/register",
    background=BackgroundTasks(
        tasks=[
            BackgroundTask(send_email_notification, message="Account created"),
            BackgroundTask(write_in_file),
        ]
    ),
)
async def create_user(data: UserIn) -> JSONResponse:
    JSONResponse({"message": "Created"})

Через response

Добавление задач через response, вероятно, будет тем способом, который вы будете использовать чаще всего, иногда вам понадобится какая-то конкретная информация, которая доступна только внутри вашего представления.

Это достигается аналогичным образом, как и в handlers.

Использование одного экземпляра

Таким же образом, как вы создавали одну фоновую задачу для handlers, в response это работает аналогично.

from typing import Dict

from pydantic import BaseModel

from esmerald import BackgroundTask, Request, Response, post


class UserIn(BaseModel):
    email: str
    password: str


async def send_email_notification(email: str, message: str):
    """Sends an email notification"""
    send_notification(email, message)


@post("/register")
async def create_user(data: UserIn, request: Request) -> Response(Dict[str, str]):
    return Response(
        {"message": "Email sent"},
        background=BackgroundTask(
            send_email_notification,
            email=request.user.email,
            message="Thank you for registering.",
        ),
    )

Использование списка

То же самое происходит при выполнении более одной фоновой задачи и когда требуется более одной операции.

from datetime import datetime
from typing import Dict

from pydantic import BaseModel

from esmerald import BackgroundTask, BackgroundTasks, Request, Response, post


class UserIn(BaseModel):
    email: str
    password: str


async def send_email_notification(email: str, message: str):
    """Sends an email notification"""
    send_notification(email, message)


def write_in_file(email: str):
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now} to: {email}"
        log.write(content)


@post("/register")
async def create_user(data: UserIn, request: Request) -> Response(Dict[str, str]):
    return Response(
        {"message": "Email sent"},
        background=BackgroundTasks(
            tasks=[
                BackgroundTask(
                    send_email_notification,
                    email=request.user.email,
                    message="Thank you for registering.",
                ),
                BackgroundTask(write_in_file, email=request.user.email),
            ]
        ),
    )

Использование add_task

Другой способ добавления нескольких задач - использование функции add_task, предоставляемой объектом BackgroundTasks.

from datetime import datetime
from typing import Dict

from pydantic import BaseModel

from esmerald import BackgroundTasks, Request, Response, post


class UserIn(BaseModel):
    email: str
    password: str


async def send_email_notification(email: str, message: str):
    """Sends an email notification"""
    send_notification(email, message)


def write_in_file(email: str):
    with open("log.txt", mode="w") as log:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        content = f"Notification sent @ {now} to: {email}"
        log.write(content)


@post("/register")
async def create_user(data: UserIn, request: Request) -> Response(Dict[str, str]):
    background_tasks = BackgroundTasks()
    background_tasks.add_task(
        send_email_notification, email=request.user.email, message="Thank you for registering."
    )
    background_tasks.add_task(write_in_file, email=request.user.email)

    return Response({"message": "Email sent"}, background=background_tasks)

Функция .add_task() принимает в качестве аргументов:

  • Функцию задачи, которая должна быть выполнена в фоновом режиме (send_email_notification и write_in_file).
  • Любую последовательность аргументов (*args), которые должны быть переданы функции задачи (email, message).
  • Любые именованные аргументы (**kwargs), которые должны быть переданы функции задачи.

Техническая информация

Классы BackgroundTask и BackgroundTasks приходят напрямую из lilya.background. Это означает, что вы можете импортировать их напрямую из Lilya, если хотите.

Esmerald импортирует эти классы и добавляет некоторую дополнительную информацию о типах, но не влияет на общую функциональность ядра.

Вы можете использовать функции def или async def при объявлении этих функций для передачи в BackgroundTask, Esmerald будет знать, как обработать их.

Для получения дополнительной информации об этих объектах вы можете ознакомиться с официальной документацией Lilya по фоновым задачам.

Справочник API

Узнайте больше о BackgroundTask, ознакомившись со Справочником API.