문제점

아래의 함수는 emails가 많을수록 문제가 아주 많아진다. 왜냐? 서버 프로세스를 점유(Block)하기 때문이다.

만약 Gunicorn Worker가 3개 있다고 가정할때, 3명의 유저가 동시에 send_emails를 수행하면 모든 프로세스는 Block되기 때문에 더이상 서비스가 불가능해진다. 아주 문제가 심각하다.

이 문제를 async Celery task로 변경해 웹프로세스 Block을 막아보자

def send_emails(request):
    emails: list[str] = request.data.get('emails')
    for email in emails:
        requests.post(URL, data=email)        
    return Response(status=200)

 

WorkFlow

1. send_emails에서 async celery task 호출

2. 메시지 브로커가 방금 호출한 task를 받아서 celery worker로 넘긴다.

3. celery worker가 일을 처리하고 result backend에 결과를 넣어준다.

출처 : https://testdriven.io/

메시지브로커, result backend로 Redis, rabbitmq 등을 많이 사용한다.

 

Celery 사용

from typing import List

import requests
from celery import shared_task
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt


@shared_task()
def task(emails: List[str]) -> None:
    for _ in emails:
        requests.post('https://httpbin.org/delay/1')


@csrf_exempt
def send_emails(request):
    emails: List[str] = request.POST.get('emails')
    task_id = task.delay(['1.naver.com', '2.naver.com', '3.naver.com'])
    return JsonResponse({'task_id': task_id.id})
    
    
def task_status(request):
    task_id = request.GET.get('task_id')

    if task_id:
        task: AsyncResult = AsyncResult(task_id)
        if task.state == 'FAILURE':
            error = str(task.result)
            response = {
                'state': task.state,
                'error': error,
            }
        else:
            response = {
                'state': task.state,
            }
        return JsonResponse(response)

위와 같이 view에서 .delay 함수를 사용해서 호출해 주면 celery에 의해서 async하게 작동한다.

즉 email들이 다 처리되는걸 기다리지 않고(non-block) 일단 Response를 반환한다.

그럼 해당 Task가 성공적으로 처리 됐는지 어떻게 판단하냐? 

Result Backend에서 해당 Task가 지금 처리됐는지 아닌지 확인하면 된다. 그러므로 반환 시 해당 Task의 id를 던져주고 프론트단에서 해당 task_status를 지속적으로 호출해 해당 작업이 완료됐는지 확인한다.

 

결론

시간이 오래걸리거나, 네트워크 io 작업이 있는 작업은 view에서 바로 처리하지말자. 반환 속도도 느려지고, gunicorn worker도 block되기 때문에 좋지 않다.

 

코드 github : https://github.com/seunwoolee/celery_test 

+ Recent posts