BackgroundTasks

사용자가 데이터를 요청하고, 서버가 이 데이터를 처리하는 데 시간이 걸리는 경우,

서버는 먼저 HTTP 응답을 반환하고, 그 후에 백그라운드에서 데이터 처리를 계속할 수 있다.

이렇게 하면 사용자는 서버의 응답을 빠르게 받을 수 있다.

 

예시 코드를 보자.

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

def write_log(message: str):
    with open("log.txt", "a") as file:
        file.write(message)

@app.post("/send/{message}")
async def send_message(background_tasks: BackgroundTasks, message: str):
    background_tasks.add_task(write_log, message)
    return {"message": "Message received"}

FastAPI는 본질적으로 비동기 프레임워크다.

Starlette(비동기 웹 프레임워크) 위에 구축되어 있으며,

따라서 Python의 async와 await 키워드를 사용하여 동시에 여러 요청을 처리할 수 있다.

 

FastAPI의 BackgroundTasks는 각 작업을 별도의 스레드에서 실행한다.

즉, BackgroundTasks.add_task() 메소드에 전달된 함수는 메인 스레드(=HTTP 요청을 처리하는 스레드)와는

별도의 스레드에서 실행된다.

이렇게 하면, 메인 스레드는 블로킹되지 않고(=I/O작업이 완료되기를 기다리지 않고) 다른 요청을 계속 처리할 수 있다.

 

 

하지만

복잡한 계산이나 작업을 수행하는 경우 동일한 프로세스의 일부로 실행하지 않는 것이 좋다.

따라서 이러한 복잡한 작업을 별도의 프로세스로 수행하려면 celery를 사용하면 된다.

celery

파이썬에서 사용하는 비동기 작업 큐/작업 스케줄링 시스템이다.

메시지 브로커를 통해 메시지를 전달하고, 작업자(worker) 프로세스가 이 메시지(일반적으로 작업 또는 태스크)를 가져와서 처리한다.

 

- 시간이 오래 걸리는 작업을 백그라운드에서 처리해야 할 때

- 많은 양의 작업을 병렬로 처리해야 할 때

- 정기적으로 반복되는 작업을 스케줄링해야 할 때

 

주요 구성 요소

  1. Producer: 작업을 만들고 메시지 브로커에 전송하는 파트입니다. 일반적으로 웹 애플리케이션에서 요청을 처리하는 동안 작업을 생성하고 큐에 넣습니다.
  2. Broker: 작업 메시지를 큐에 보관하고, 작업자에게 전달하는 중개자 역할을 합니다. RabbitMQ, Redis 등 다양한 메시지 브로커를 사용할 수 있습니다.
  3. Worker: 실제로 작업을 처리하는 프로세스입니다. 브로커로부터 작업 메시지를 받아 처리하고 결과를 저장합니다.
  4. Backend: 작업의 결과를 저장하는 곳입니다. 이는 선택 사항이며, 작업의 결과를 추적하거나, 작업이 성공적으로 완료되었는지 확인하려는 경우에 사용됩니다.

celery는 이런 구조를 통해 큰 규모의 분산 작업 처리를 가능하게 한다.

병렬 처리, 작업 우선 순위, 스케줄링 등 다양한 기능을 제공하며

신뢰성있는 작업 처리를 위한 실패 시 재시도, 작업 결과 추적 등의 기능도 제공하다.

 

 

FasAPI, Celery, RMQ 예제

https://medium.com/cuddle-ai/async-architecture-with-fastapi-celery-and-rabbitmq-c7d029030377

 

Async Architecture with FastAPI, Celery, and RabbitMQ

In one of my earlier tutorials, we have seen how we can optimize the performance of a FastAPI application using Async IO. To know more you…

medium.com

 

 

BackgroundTask와 Celery는 모두 비동기 작업을 처리하는 데 사용되지만

차이점은 아래와 같다.

 

BackgroundTasks

동일한 프로세스 내에서 별도의 스레드에서 실행되며, 작업이 완료 될 때까지 메인 스레드의 실행을 차단하지 않는다.

그러나 Python의 Global Interpreter Lock(GIL) 때문에

CPU-bound 작업에서는 이 방식이 큰 성능 향상을 가져오지 못할 수 있다.

 

Celery

별도의 작업자(worker) 프로세스에서 비동기 작업을 처리하는데 사용된다.

이는 메시지 브로커를 통해 메인 애플리케이션과 작업자 프로세스 간에 작업을 전달한다.

따라서 복잡하거나 시간이 오래 걸리는 작업, 또는 대량의 작업을 병렬로 처리하는데 적합하다.

또한 각 작업자 프로세스는 독립적으로 실행되므로, GIL에 의한 제한을 받지 않는다.

 

결국 BackgroundTasks는 간단하고 빠른 작업에 적합하고,

Celery는 복잡하거나 오래 걸리는 작업에 적합하다.