어떻게 읽어야할지 무언가 생소한 이 단어는, '샐러리'라고 보통 읽는듯 하다.
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에 남아있습니다.