๐[Django] ์ง์ง ๊ฐ๋จํ Celery ์คํ
Celery ์คํ ์์
Celery
์ ๋ํด์ ์ดํด๋ ๋์ง๋ง, ์ ์ ์ ์ฉํ๋ ค๊ณ ๋ณด๋ ์ด๋ป๊ฒ ๋์ํ๊ณ ์ด๋ป๊ฒ ๊ณ ๋ํ ํด์ผํ๋ ๊ฑด์ง ๋ ์์ด ์บ์บํ๋ค.
๋ฌด์์ด๋ ์ง ์์์ด ๋ฐ์ด๋๊น, 1+1 ์์ค์ ๊ฐ๋จํ ์คํ์ผ๋ก ์์ํด๋ณด๋ ค๊ณ ํ๋ค.
๐ย 1์ด๋ง๋ค 1์ฉ ๊ฐ์ ์ฆ๊ฐ์ํค๊ณ , ๊ทธ ๊ฐ์ ํ์ธํ๋ ์ฑ์ด๋ค!
Django project & app
mkdir celery_test
cd celery_test
celery_test
๋ผ๋ ํด๋๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ํด๋๋ก ์ ์ํ๋ค.- ๊ฐ์ธ์ ์ผ๋ก ํด๋ ์๋์ ํ๋ก์ ํธ๋ฅผ
config
๋ผ๋ ์ด๋ฆ์ผ๋ก ์์ฑํ๋ ๊ฒ์ ์ ํธํจ.
- ๊ฐ์ธ์ ์ผ๋ก ํด๋ ์๋์ ํ๋ก์ ํธ๋ฅผ
django-admin startproject config .
- django project๋ฅผ ์์ฑํ๋ค.
python manage.py startapp api
api
๋ผ๋ ์ด๋ฆ์ ์ฑ์ ์์ฑํ๋ค.
celery & redis install
python -m venv venv
source venv/bin/activate
- ๊ฐ์ํ๊ฒฝ์ ๋ง๋ค๊ณ , ์คํํ๋ค.
pip install celery
- django 3.1 ๋ถํฐ๋ ๊ธฐ๋ณธ์ผ๋ก ์ง์๋๋ค๊ณ ํ๋ค.
pip install redis
- redis๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์ค์นํด์ค๋ค.
- ์ redis? โ ์ง์ง ๋๋ฌด ๊ฐ๋จํ ์คํ์ด๋ผ์ ๋น๊ต์ ์ค์ ์ด ์ฌ์ด redis ์ ํ
config/settings.py
INSTALLED_APPS = [
...
"api",
]
# Redis and Celery settings
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
REDIS_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}'
CELERY_BROKER_URL = REDIS_URL
CELERY_RESULT_BACKEND = REDIS_URL
- ์์ฑํ ์ฑ์ ์ถ๊ฐํด์ฃผ๊ณ
- redis ์ธํ ์ ์ํด ์ถ๊ฐํด์ค๋ค.
config/celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from celery.schedules import crontab
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
app = Celery('config')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print(f'Request: {self.request!r}')
app.conf.beat_schedule = {
'increment-every-second': {
'task': 'api.tasks.increment_number',
'schedule': 1.0, # 1 second
},
}
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{PROJECT_NAME}.settings')
: ํ๊ฒฝ๋ณ์ ์ธํ ์ด๋ค. โ{PROJECT_NAME}
์๋ ํ๋ก์ ํธ์ ์ด๋ฆ์ด ๋ค์ด๊ฐ๋ฉด ๋๋ค. (๋config
๊ฐ ๋ค์ด๊ฐ)app = Celery('{PROJECT_NAME}')
:config
๋ผ๋ ์ด๋ฆ์ผ๋ก Celery ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.- ๊ทธ ์๋๋ ์ ๋ฌ๋ฆฌ ๊ธฐ๋ณธ ์ธํ ์ด๋ค.
app.conf.beat_schedule
: ์ ๋ฌ๋ฆฌ ๋นํธ ์ค์ ์ด๋ค. ์ค์ ์ค์ผ์ค๋ง์ ๋๋ฆด ์๊ฐ์ ๊ธฐ์ค์ ์ค์ ํ๋ค. ์ฌ๊ธฐ์๋ 1์ด.
api/tasks.py
import redis
from celery import shared_task
from django.conf import settings
# Redis์ ์ฐ๊ฒฐ
r = redis.Redis.from_url(settings.REDIS_URL)
@shared_task
def increment_number():
# ํ์ฌ ๊ฐ์ ๊ฐ์ ธ์ต๋๋ค. ๊ฐ์ด ์์ผ๋ฉด 0์ผ๋ก ์ด๊ธฐํํฉ๋๋ค.
current_value = r.get('current_number')
if current_value is None:
current_value = 0
else:
current_value = int(current_value)
# ๊ฐ์ 1์ฉ ์ฆ๊ฐ์ํต๋๋ค.
current_value += 1
# Redis์ ์ ์ฅํฉ๋๋ค.
r.set('current_number', current_value)
return current_value
r = redis.Redis.from_url(settings.REDIS_URL)
: redis์ ์ฐ๊ฒฐํ๋ ์ค์ ์ด๋ค.@shared_task
: Celery์ ์์ ์์ ๋ํ๋ธ๋ค. ์ด ์์ ์Worker
๋ก ์คํ๋ ์ ์๋ค.- ์๋ ํจ์๋ task์ ์ด๋ฆ์ด๊ฒ ์ง
- redis์
current_number
๋ผ๋ ์ปฌ๋ผ ๊ฐ์ ๊ฐ์ ธ์จ๋ค. - ๋ง์ฝ ์์ผ๋ฉด 0์ผ๋ก ์ด๊ธฐํํ๋ค.
- ๋ณธ๊ฒฉ์ ์ธ task ์์์ผ๋ก,
current_value
๋ฅผ 1์ฉ ์ฆ๊ฐํ๋ค. - ์ฆ๊ฐํ ๊ฐ์
r.set("current_number", current_value)
๋ฅผ ํตํด redis์ ์ ์ฅํ๋ค. - ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํดํ๋ค.
api/views.py
from django.http import JsonResponse
import redis
from django.conf import settings
r = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB)
def show_hello(request):
current_value = r.get('counter')
if current_value is None:
current_value = 0
else:
current_value = int(current_value)
return JsonResponse({'counter': current_value})
r = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB)
: redis ์ฐ๊ฒฐ์ ๋ถ๋ฌ์จ๋ค.- ์๊น๋ณด๋ค ๊ธธ์ด์ก๋๋ฐ, ๊ฒฐ๊ตญ ๋๊ฐ์๊ฑฐ์.
settings.py
๋ฅผ ํ์ธํด๋ณด์.
- ์๊น๋ณด๋ค ๊ธธ์ด์ก๋๋ฐ, ๊ฒฐ๊ตญ ๋๊ฐ์๊ฑฐ์.
current_value = r.get('counter')
: redis์์counter
๋ผ๋ ํค์ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.- ์ฌ๊ธฐ์ ์ ์ ์๋๊ฒ,
tasks.py
์์ redis์ ๊ฐ์ ์ ์ฅํ ๋,{counter : current_number}
๋ก ์ ์ฅํ๊ฒ ๊ตฌ๋ ์ถ์๊ฑฐ์.
- ์ฌ๊ธฐ์ ์ ์ ์๋๊ฒ,
current_value
๊ฐ์ ๊ฐ์ ธ์ค๊ณ , ์์ผ๋ฉด 0์ผ๋ก ์ด๊ธฐํ, ์์ผ๋ฉด int๋ก ํ๋ณํ ํ JSON ์๋ต์ ๋ด๋ณด๋ธ๋ค.
์คํํ๊ธฐ
์ผ๋จ, ๊ต์ฅํ ๋ง์ ํฐ๋ฏธ๋์ ์ฌ์ฉํ๋ค..
๋ชจ๋ ๋ช
๋ น์ ๋ฃจํธ ๋๋ ํ ๋ฆฌ(manage.py๊ฐ ์กด์ฌํ๋ ์์น)
์์ ์งํํ๋ค.
1. redis ์คํํ๊ธฐ
redis-server
- ๋ง์ฝ ํฌํธ๊ฐ ์ฌ์ฉ์ค์ด๋ฉด?
lsof -i :6379
โsudo kill -9 [PID]
2. redis cli ์คํํ๊ธฐ
redis-cli
- ์ ์ฅ๋ ๊ฐ ๋ณด๊ณ ์ถ์ผ๋ฉด?
GET current_number
3. Celery worker ์คํํ๊ธฐ
celery -A config worker --loglevel=info
4. Celery Beat ์คํํ๊ธฐ
celery -A config beat --loglevel=info
5. runserver
python manage.py runserver
6. api ์ ์ํด์ ํ์ธ
๋๋ url์ http://127.0.0.1:8000/api/show_hello/
๋ก ์ค์ ํ๋ค.
๊ถ๊ธํ ๋ด์ฉ
api/[tasks.py](http://tasks.py)
์์ current_value๋ฅผ return ํ๋๋ฐ, api/views.py
์์ redis๋ฅผ ์ฐ๊ฒฐํ๊ณ , ๊ฑฐ๊ธฐ์ ๊ฐ์ get
ํ๋ ๊ฒ์ด ์๋๋ผ, tasks.py์ ๊ฐ์ ๋ถ๋ฌ์ค๋ฉด ๋ ๊น?
์ฌ์ค ๋น์ฐํ ๋ผ์ผํ๋๊ฑฐ๋ผ๊ณ ์๊ฐํ๊ธด ํ์ง๋ง ์ผ๋จ ํ ์คํธ ์งํํ์.
api/views.py
from django.http import JsonResponse
import redis
from django.conf import settings
# r = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB)
# def show_hello(request):
# current_value = r.get('counter')
# if current_value is None:
# current_value = 0
# else:
# current_value = int(current_value)
# return JsonResponse({'counter': current_value})
from .tasks import increment_number
def show_hello(request):
current_value = increment_number()
if current_value is None:
current_value = 0
else:
current_value = int(current_value)
return JsonResponse({'counter': current_value})
- ์ด์ ๋ด์ฉ์ ๊ทธ๋ฅ ์ผ๋จ ์ฃผ์์ฒ๋ฆฌํ๊ณ ,
increment_number
๋ฅผ importํด์ ์คํํ๋ค. - ์ ์์ ์ผ๋ก ์ซ์๊ฐ ์ฌ๋ผ๊ฐ๋ค.
๋๊ธ๋จ๊ธฐ๊ธฐ