2 λΆ„ μ†Œμš”

Celeryλž€?

Celery λž€, λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ—μ„œ κ°€μž₯ 많이 μ‚¬μš©ν•˜λŠ” 기법 쀑 ν•˜λ‚˜μž„.

λΆ„μ‚° λ©”μ‹œμ§€ 전달을 기반으둜 λ™μž‘ν•˜λŠ” 비동기 μž‘μ—… 큐 라고 ν•œλ‹€.

  • 즉, μ‹€μ‹œκ°„ μ²˜λ¦¬μ— 쀑점을 두고 μž‘μ—… μ˜ˆμ•½μ„ μ§€μ›ν•˜λŠ” μž‘μ—… 큐 μΈκ±°μž„!!
    • μŠ€μΌ€μ€„λ§λ„ μ§€μ›ν•˜κΈ΄ 함

CeleryλŠ” λ©”μ‹œμ§€λ₯Ό μ „λ‹¬ν•˜λŠ” μ—­ν• (Publisher)κ³Ό λ©”μ‹œμ§€λ₯Ό Message Broker μ—μ„œ 가져와 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” Worker 의 역할을 λ‹΄λ‹Ήν•œλ‹€.

Worker?

Worker λŠ” μ›Ή μ„œλΉ„μŠ€μ—μ„œ λ°±μ—”λ“œμ˜ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” λ³„λ„μ˜ Frame μž„!

이둜 인해 μ‚¬μš©μžμ—κ²Œ 즉각적인 λ°˜μ‘μ„ 보여쀄 ν•„μš”κ°€ μ—†λŠ” μž‘μ—…λ“€λ‘œ 인해 μ‚¬μš©μžκ°€ λŠλΌλŠ” μ‹œκ°„ 지연을 μ΅œμ†Œν™” ν•  수 있음.

이처럼 Celery λ₯Ό μ‚¬μš©ν•˜λ©΄, API μ„œλ²„λ₯Ό κ°œλ°œν•˜κ³  μš΄μ˜ν•˜λ©΄μ„œ μ‚¬μš©μž μš”μ²­μ— 포함될 ν•„μš”κ°€ μ—†λŠ” λΆˆν•„μš”ν•œ κ³Όμ •μ΄λ‚˜ 무거운 쿼리 μ‹€ν–‰ λ“±μ˜ μž‘μ—…μ„ Celery Task 둜 μ •μ˜ν•΄μ„œ Broker(RabbitMQ, Redis λ“±) 와 Consumer(Celery Worker) λ₯Ό μ΄μš©ν•΄ 비동기 μ²˜λ¦¬ν•˜μ—¬ μ‚¬μš©μžμ—κ²Œ λΉ λ₯΄κ²Œ 응닡을 μ œκ³΅ν•  수 μžˆλ‹€!!

[μ‚¬μš©μž μš”μ²­μ— 포함될 ν•„μš” μ—†λŠ” μž‘μ—… μ˜ˆμ‹œ]

- νšŒμ›κ°€μž… μΆ•ν•˜ 이메일 λ°œμ†‘
- μ£Όλ¬Έ λ‚΄μ—­ λ‹€μš΄λ‘œλ“œ

Celery ꡬ성 μš”μ†Œ

Broker

Broker λŠ” Task(Message)λ₯Ό Worker μ—κ²Œ μ „λ‹¬ν•˜λŠ” μ—­ν• μž„

Client

Client λŠ” Taskλ₯Ό 생성 ν•˜λŠ” μ—­ν• μž„

Worker

Worker λŠ” Taskλ₯Ό μ‹€ν–‰ν•˜κ³  μ²˜λ¦¬ν•˜λŠ” μ—­ν• 

Celery νŠΉμ§•?

  • Clientλ‚˜ WorkerλŠ” μ—°κ²° μœ μ‹€μ΄λ‚˜ μ‹€νŒ¨μ— λŒ€ν•΄ μžλ™μœΌλ‘œ μž¬μ‹œλ„ ν•˜λ©°, Broker, Workerλ₯Ό HA κ΅¬μ„±ν•˜μ—¬ κ³ κ°€μš©μ„±μ΄ λ›°μ–΄λ‚˜λ‹€!
    • HA ꡬ성: High Availavility β†’ κ³ κ°€μš©μ„± μ΄λΌλŠ” λœ»μž„
  • 단일 Celery ν”„λ‘œμ„ΈμŠ€λŠ” μ΅œμ ν™” 섀정을 톡해 ms λ‹¨μœ„ μ •λ„μ˜ λŒ€κΈ° μ‹œκ°„μœΌλ‘œ λΆ„λ‹Ή 수백만 개의 μž‘μ—…μ„ μ²˜λ¦¬ν•  수 μžˆμ„ μ •λ„λ‘œ 빠름
  • CeleryλŠ” ν™•μž₯성이 맀우 λ›°μ–΄λ‚˜ 거의 λͺ¨λ“  뢀뢄을 μ»€μŠ€ν…€ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆλ‹€!

Celery λ™μž‘ ꡬ쑰

image

CeleryλŠ” μ›Ή μ„œλ²„μ—μ„œ λ°œμƒν•œ μš”μ²­(Task) 을 Message Broker μ—μ„œ λ°›μ•„ Celeryλ₯Ό μ΄μš©ν•΄ λΆ„μ‚° μ²˜λ¦¬ν•¨!

Celeryμ—μ„œλŠ” μž‘μ—…μ΄ μ™„λ£Œλ˜λŠ” μ΄λ²€νŠΈμ— λŒ€ν•΄ DB Taskλ₯Ό μˆ˜ν–‰ν•¨!

Celery Broker?

Broker λŠ” Client 와 Worker μ‚¬μ΄μ—μ„œ λ©”μ‹œμ§€λ₯Ό μ „λ‹¬ν•˜λŠ” μ—­ν• μž„!

5.0 버전 κΈ°μ€€μœΌλ‘œ λ‹€μŒκ³Ό 같은 Broker λ₯Ό μ§€μ›ν•œλ‹€κ³  ν•œλ‹€.

  • RabbitMQ
    • RabbitMQ의 인증 및 인가 방식을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•  수 μžˆλ‹€!
    • 맀우 효율적 & κ΄‘λ²”μœ„ν•œ 배포 및 ν…ŒμŠ€νŠΈλœ λ©”μ‹œμ§€ λΈŒλ‘œμ»€μž„
    • κ³ κΈ‰ λΌμš°νŒ… μš”κ΅¬ 사항이 μžˆλŠ” μ—”ν„°ν”„λΌμ΄μ¦ˆ λ©”μ‹œμ§•μ„ μ œκ³΅ν•˜λŠ”λ°μ— 적합
  • Redis
    • μ‹œμž‘μ΄ λΉ λ₯΄κ³  κ°€λ²Όμš΄ Broker
    • ν•˜μ§€λ§Œ μ•ˆμ •μ μΈ 전달을 μ§€μ›ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€
    • μ‹œμŠ€ν…œμ΄ μ’…λ£Œλ˜λŠ” 경우, λͺ‡ λΆ„ λ™μ•ˆ μž‘μ—…μ— λŒ€ν•œ 정보가 μ†μ‹€λ˜λŠ” 것이 μ€‘μš”ν•˜μ§€ μ•Šμ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— 적합
    • Redis μžμ²΄κ°€ λ©”λͺ¨λ¦¬λ₯Ό μ‚¬μš©ν•˜μ—¬ μ €μž₯ν•˜λŠ” 방법이라, λ©”λͺ¨λ¦¬κ°€ λΆ€μ‘±ν•œ μƒν™©μ—μ„œλŠ” μž„μ˜λ‘œ Key κ°€ μ‚­μ œλ  수 있음 β†’ Task λ₯Ό 받아도 μ‚­μ œλ  수 있음
    • λ©”μ‹œμ§€ 손싀 방지 기법이 μ—†μŒγ…œγ…œ
  • Amazon SQS
  • Zookeeper

Celery μ‚¬μš© μ˜ˆμ‹œ

진행 쀑인 ν”„λ‘œμ νŠΈμ—μ„œ Celeryλ₯Ό μ‚¬μš©ν•˜κ²Œ λ˜μ—ˆλ‹€.

처음 μ¨λ³΄λŠ” 기술이기 λ•Œλ¬Έμ— μ…‹μ—…μ‘°μ°¨ λ‚―μ„€κ³  μ–΄λ €μ› λ‹€.

λ‹€μŒμ€ ν”„λ‘œμ νŠΈμ— μΆ”κ°€ν•œ Celery μ…‹μ—… μ½”λ“œμ΄λ‹€.

config/celery.py

import os
from celery import Celery
from celery.schedules import crontab

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

app = Celery("hellotarot")
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print("Request: {0!r}".format(self.request))

app.conf.beat_schedule = {
    "update-status-5-minutes": {
        "task": "check_game_status",
        "schedule": crontab(second="*/1")
    }
}

μ½”λ“œ 해석

import os
from celery import Celery
from celery.schedules import crontab

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

app = Celery("hellotarot")
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
  • crontab: μ…€λŸ¬λ¦¬ μŠ€μΌ€μ€„μ—μ„œ cron μž‘μ—…κ³Ό μœ μ‚¬ν•œ μŠ€μΌ€μ€„μ„ μ •μ˜ν•˜λŠ” 데 μ‚¬μš©ν•œλ‹€.
    • cron: μœ λ‹‰μŠ€ 기반 μ‹œμŠ€ν…œμ—μ„œ μ‹œκ°„ 기반의 μž‘μ—… μŠ€μΌ€μ€„λŸ¬λ‘œ, μ‚¬μš©μžκ°€ μ§€μ •ν•œ μ‹œκ°„μ— μžλ™μœΌλ‘œ 슀크립트 μ‹€ν–‰, λͺ…λ Ήμ–΄ μ‹€ν–‰ λ“±μ˜ 적업을 반볡적으둜 μˆ˜ν–‰ν•˜λ„λ‘ μ˜ˆμ•½ν•œλ‹€.
  • κΈ°λ³Έ Django μ„€μ • λͺ¨λ“ˆμ„ config.settings둜 μ„€μ •ν•œλ‹€.
    • config디렉토리에 settings.pyκ°€ 있고, ν•΄λ‹Ή νŒŒμΌμ— ν”„λ‘œμ νŠΈ ꡬ성이 ν¬ν•¨λ˜μ–΄ μžˆμ–΄μ•Ό ν•œλ‹€.
  • hellotarotμ΄λΌλŠ” μ΄λ¦„μœΌλ‘œ Celery μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•œλ‹€.
  • Django ν”„λ‘œμ νŠΈ μ„€μ •μ—μ„œ Celery 섀정을 λ‘œλ“œν•œλ‹€. 특히 CELERYλ„€μž„μŠ€νŽ˜μ΄μŠ€ λ‚΄μ˜ 섀정을 λ‘œλ“œν•œλ‹€.
  • μ΄λ ‡κ²Œ ν•˜λ©΄ Django μ„€μ •μ—μ„œ μ •μ˜ν•œ λͺ¨λ“  μ‚¬μš©μž 지정 Celery 섀정이 μ μš©λœλ‹€.
  • ν”„λ‘œμ νŠΈ λ‚΄μ—μ„œ μž‘μ—… μ •μ˜(@app.task 둜 λ°μ½”λ ˆμ΄μ…˜λœ ν•¨μˆ˜)λ₯Ό μžλ™μœΌλ‘œ κ²€μƒ‰ν•˜μ—¬ μž‘μ—… 등둝을 κ°„μ†Œν™” ν•œλ‹€!
@app.task(bind=True)
def debug_task(self):
Β Β print("Request: {0!r}".format(self.request))
  • @app.task(bind=True): debug_taskλΌλŠ” μ΄λ¦„μ˜ μž‘μ—…μ„ μ •μ˜ν•¨. 첫 번째 인수둜 selfλ₯Ό ν—ˆμš©ν•˜κ³ , bind=True λ°μ½”λ ˆμ΄ν„°λŠ” μž‘μ—… 메타데이터(self 인수)에 λŒ€ν•œ μ—‘μ„ΈμŠ€λ₯Ό ν—ˆμš©ν•œλ‹€.
  • print("Request: {0!r}".format(self.request)): μž‘μ—… λ‚΄μ—μ„œ μž‘μ—… μš”μ²­μ˜ μ„ΈλΆ€ 정보λ₯Ό 좜λ ₯ν•˜λŠ” μš©λ„μ΄λ©°, 디버깅에 μš©μ΄ν•˜κ²Œ 쓰인닀.
app.conf.beat_schedule = {
Β Β "update-status-5-minutes": {
Β Β Β Β "task": "check_game_status",
Β Β Β Β "schedule": crontab(second="*/1")
Β Β }
}
  • app.conf.beat_schedule: 이 사전은 Celery Beatλ₯Ό μ‚¬μš©ν•˜μ—¬ 주기적인 μž‘μ—…μ„ μœ„ν•œ μŠ€μΌ€μ€„μ„ μ •μ˜ν•œλ‹€.
    • update-status-5-minutes: μ˜ˆμ•½λœ μž‘μ—…μ˜ μ΄λ¦„μž„
      • task: μ‹€ν–‰ν•  μž‘μ—…μ˜ μ΄λ¦„μž„
        • schedule: crontab을 μ‚¬μš©ν•΄ μŠ€μΌ€μ€„μ„ μ •μ˜ν•¨
          • crontab(second="*/1"): μž‘μ—…μ„ 1μ΄ˆλ§ˆλ‹€ μ‹€ν–‰ (second, minute, hour μ„€μ • κ°€λŠ₯)

참고 자료

[Celery] Python - Celeryλž€?

Python Celeryλž€

νƒœκ·Έ:

μΉ΄ν…Œκ³ λ¦¬:

μ—…λ°μ΄νŠΈ:

λŒ“κΈ€λ‚¨κΈ°κΈ°