어떻게 읽어야할지 무언가 생소한 이 단어는, '샐러리'라고 보통 읽는듯 하다.
Django에서는 View의 처리가 완료되어야 Render가 시작된다. 하지만 Heavy한 작업이 필요한 경우 View에서 작업이 지연된다면, 사용자가 응답받을때까지 시간이 지연된다. 따라서 비동기적으로 작업을 처리할 수 있는 방법이 필요하다.
이러한 비동기적인 작업을 처리하는 방식에서, 작업(Job)을 만드는 발행자(Publisher)와 작업자(Worker)의 역활이 생긴다.
Django에서 Celery를 통해 작업(Job)을 발행하면, 이 작업은 작업대인 메시지 큐에 쌓인다. 이 메시지 큐로는 RabbitMQ 혹은 Redis가 사용된다.
작업자는 작업대(메시지큐)의 메시지(작업)을 소비하고, 메시지큐에 작업이 완료되었다는 표식을 남긴다. 이러한 과정을 도와주는 역활이 샐러리이다. 그중 작업대 역활을 하는 메시지 큐는 브로커라고 불린다.
이번 프로젝트는 API 콜을 하는데, API의 응답속도가 빠르지 않았다. 따라서 정보를 DB에 저장해주어야 할 필요가 있었다. 비동기적 작업을 하지 않을경우 이러한 작업이 사용자에게 응답이 만들어 지기전 이루어져, 사용자의 응답은 느려지게된다.
따라서 아래의 프로세스를 통해 사용자경험을 극대화하려한다. Celery를 사용하지 않으면 View가 종료되기 전 5번이 실행되어야하며, 이러한 과정에서 DB의 병목 및 응답시간 증가가 해소될 것으로 보인다.
1.사용자 요청
2.사용자 요청에 대해 API Call 이전 보유여부 확인
2-1) 사용자 요청에 맞는 정보가 Cache에 있는가?
2-2) 사용자 요청에 맞는 정보가 DB에 있는가?
3.Caching/DB에 없으면 API Call을 통해 정보를 갱신한다.
4.받아온 정보를 재가공하여 사용자에게 전달한다. (View 종료)
5. 데이터가공, DB 저장 및 Cache를 위한 Celery 테스크를 발행한다.
도움을 받은 글들
장고(Django)에서 셀러리(Celery) 사용하기 1편
Celery 공식 문서
기존에는 Django와 결합된 별도의 패키지를 설치해야했지만, 이제는 Celery Stand-only로 설치가능하다.
pip install celery
pip install redis
pip install django-celery-beat
pip install django-celery-results
django-celery-beat는 특정한 기간마다 작업이 실행하게하는 인터페이스,
django-celery-results는 작업결과를 알려줍니다.
settings.py에서 아래 설정을 추가합니다.
INSTALLED_APPS = [
....
'django_celery_beat',
'django_celery_results',
...
]
# Celery
CELERY_ALWAYS_EAGER = True
CELERY_BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TAST_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Seoul'
Settings.py가 있는 프로젝트 (App이 아님!) 폴더에 생성해야합니다.
- proj/
- manage.py
- proj/ <이 폴더의 하위폴더!
- __init__.py
- settings.py
- urls.py
import os
from celery import Celery
# Set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')
app = Celery('proj')
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
@app.task(bind=True, ignore_result=True)
def debug_task(self):
print(f'Request: {self.request!r}')
proj/proj/__init__.py:
from .celery import app as celery_app
__all__ = ('celery_app',)
celery -A proj worker -l INFO
manage.py가 있는 폴더에서 실행해야합니다. 그렇지 않는다면 Module 'proj' has no attribute 'celery' 같은 에러가 나옵니다.


잘 되었다면, python3 maange.py shell을 통해 날린 add 명령이 잘 동작해야합니다.
REDIS CLI를 통해 어떻게 명령이 들어갔는지 확인해봅시다. REDIS 설정하기를 통해 설정한 Docker Redis 기반입니다.
docker run -it --network redis-net --rm redis redis-cli -h django_redis
scan 0
KEYS *을 사용하는 방법도 있지만, Redis는 Single-Thread 기반이여서, 재귀적으로 호출하는 scan0 를 사용하는것을 권장합니다.
GET [내가 찾은 celery task 키]

값이 잘 들어간 것을 확인할 수 있습니다.
res = add.apply_async((1,2), ignore_result=True)
res.get()
res = add.apply_async((9,10))
res.get()
실제 실행결과는 아래와 같습니다.

Celery는 잘 실행을 했지만, 해당 결과값에 대해 저장을 하지 않으므로 값은 Redis에 남지 않습니다.

실제로 마지막에 실행한 0b89로 끝나는 Task만 Redis에 남아있습니다.
