서비스를 만들면서 피할 수 없는 주제 중 한가지가 바로 비동기 처리입니다. 무겁고 오래 걸리는 일에 대한 처리뿐 아니라, 주기적으로 수행해야 하는 일까지 대부분 서비스에 반드시 라고 할 만큼 겪게 되는 문제죠. Python을 쓰는 우리에게는 물론 싱싱하고 훌륭한 해법인 Celery가 있습니다. 요구되는 거의 모든 기능을 제공할 뿐만 아니라, 유연하게 설계되어 있고 관리툴 같은 부가 기능까지, 비동기에 관련된 모든 부분을 책임져주죠.
하지만 Celery에 이런 빛과 같은 아름다움만 존재하는 것은 아닙니다. 싱싱한 채소를 맛있게 먹기 위해서는 몇 가지 공부가 필요한 것처럼, 때로는 Celery의 의아스러운 점을 잘 다루고, 우리의 서비스에 맞게 이용하기 위해서는 몇 가지 알아야 할 점이 있습니다. 지난 1년여간 최대 1만 건/초의 요청을 Celery로 처리하면서 제가 얻은 경험을 나누고자 합니다.
9. 비동기 처리기는 왜 필요한걸까?
고갱님 어떤 서비스
진지하게 가입을 해볼까?
이메일, 이름, .. 좋아 가입!
10. 비동기 처리기는 왜 필요한걸까?
어떤 서비스
진지하게 가입을 해볼까?
이메일, 이름, .. 좋아 가입!
에.. 고객님 프로필 사진을
업로드하고.. 크기도 적당히
3개쯤 준비해두고..
고갱님
11. 비동기 처리기는 왜 필요한걸까?
어떤 서비스
진지하게 가입을 해볼까?
이메일, 이름, .. 좋아 가입!
에.. 고객님 프로필 사진을
업로드하고.. 크기도 적당히
3개쯤 준비해두고.. 요즘 유
행이니까 배경 블러도 만들
어 두고…
고갱님
12. 비동기 처리기는 왜 필요한걸까?
어떤 서비스
진지하게 가입을 해볼까?
이메일, 이름, .. 좋아 가입!
에.. 고객님 프로필 사진을
업로드하고.. 크기도 적당히
3개쯤 준비해두고.. 요즘 유
행이니까 배경 블러도 만들
어 두고… 아 친구도 찾아
드려야 하는데 까먹을뻔 했
네 데헷(*^^*)..
고갱님
13. 비동기 처리기는 왜 필요한걸까?
어떤 서비스
에.. 고객님 프로필 사진을
업로드하고.. 크기도 적당히
3개쯤 준비해두고.. 요즘 유
행이니까 배경 블러도 만들
어 두고… 아 친구도 찾아
드려야 하는데 까먹을뻔 했
네 데헷(*^^*).. 자 다했다
이제 이메일 보내드려야지!
15. 비동기 처리기는 왜 필요한걸까?
• 비동기 처리기는 동기적으로 수행하지 않아도 되는 일들을 처
리해 주는 역활을 합니다.
• 즉 결과를 즉시 받을 필요 없거나, 지연하여 처리해야 되는 일
들을 보통 처리합니다.
• 물론 그것이 제대로 처리가 되지 않아도 된다는 이야기는 아
니기 때문에, 별도의 잘 만들어진 처리기가 필요해요!
81. (아마도) 당신이 상상할 수 있는 모든 기능
from celery import chain
# 2 + 2 + 4 + 8
res = chain(add.s(2, 2), add.s(4), add.s(8))()
res.get()
16
82. (아마도) 당신이 상상할 수 있는 모든 기능
from celery import chain
!
# 2 + 2 + 4 + 8
res = chain(add.s(2, 2), add.s(4), add.s(8))()
res.get()
16
!
(add.s(2, 2) | add.s(4) | add.s(8))().get()
16
83. (아마도) 당신이 상상할 수 있는 모든 기능
new_user_workflow = (create_user.s() | group(
import_contacts.s(),
send_welcome_email.s())
)
new_user_workflow.delay(username='artv',
first='Art',
last='Vandelay',
email='art@vandelay.com')
84. (아마도) 당신이 상상할 수 있는 모든 기능
from datetime import timedelta
!
CELERYBEAT_SCHEDULE = {
'add-every-30-seconds': {
'task': 'tasks.add',
'schedule': timedelta(seconds=30),
'args': (16, 16)
},
}
!
CELERY_TIMEZONE = 'UTC'
94. 잘 알고 써야 하는 Celery!
• 적은 규모에서, 간편하게 쓰기에는 더 없이 훌륭하지만…
• 의외로 조금만 규모가 커져도 신경써야 할 부분이 많아요!
• 특히 처음부터 고려하지 않으면 알 수 없는 이상작동처럼 느
껴질 수도 있으니 주의가 필요한 부분이 있어요!
112. Redis/SQS에서의 visibility timeout
• visibility timeout내에 ack가 전달되지 않으면, task가 중
복 실행됩니다.
• eta, countdown 시간보다 visibility timeout이 커야해요.
• AWS SQS의 최대 시간 제한은 12시간!
113. 그 외의 고생길(!)
• Redis는 메모리가 부족한 상황에서 임의로 key가 삭제될 수
있어요!
• SQS는 API요청당 과금을 하고, Pub/Sub이 아닌 Polling
모델 이에요. 자주 땡길(!)수록 요금이 나와요.
• SQS를 Broker로 사용하면 Celery의 주요한 모니터링 기
능 대부분을 사용할 수 없어요.
121. Prefetch is a term inherited
from AMQP that is often
misunderstood by users.
122. The prefetch limit is a limit for
the number of tasks (messages)
a worker can reserve for itself
123. S S S L S S S S
S S S L
prefetch : 4
L / / /
queue size : 8
S S S S
concurrency: 1
124. 오해받기 쉬운 그/그녀 prefetch….
• prefetch된 단위 전체의 작업을 소비해야(ack*) 다음
prefetch가 수행 됩니다.
• task가 비워지는 대로 다음 task를 broker에서 가져올꺼라
는 일반적인 기대와 차이가 있어요!(often
misunderstood)
• 물론 task payload의 크기가 적고, latency가 중요하다면
합리적인 결정이기도 합니다.
132. Task를 한 큐에 담지 마세요!
• prefetch의 특성상 평균 수행 시간이 비슷한 것들이 같은
Queue에 있는 것이 성능상 훨씬 유리합니다.
• 또한 Task의 절대적인 수 자체도 중요한 요소에요.
• 처리의 중요도/시급도(Priority)에 따른 분류도 중요해요.
• 위와 같은 요소를 고려해서 Queue를 나눠 주세요
165. ignore_result
• Celery는 기본적으로 수행 결과(return)를 ‘저장’ 해야 작업
이 끝납니다.
• 하지만 대부분 Task내에서 직접 결과를 다른 곳에 저장하지,
`return` 자체를 쓰는 경우는 드물어요!
• 보통은 Task 연계를 하는 경우만 필요하답니다!
• 결과를 저장하는 비용이 적지 않기 때문에 이걸 끄기만 해도
무척 성능이 좋아져요!
169. 그래도 역시 Celery!
• 비록 여러 어려움을 설명하긴 했지만, 이는 꼭 잘못된 디자인
/구현 이라기 보다는 여러 제약속에서 특정 선택을 했을 뿐인
경우가 많아요!
• Celery 없이 문제를 해결하려다 보면 결국 바퀴를 다시 만들
게 되는 경우가 많아요!
• Celery는 요집도 조집도 우리집도 너희집도 많이 쓰기 때문
에 같이 어려운 문제를 같이 해결해 줄 사람이 많아요!
• 본래 ‘분산’ ‘비동기’ 이런 단어 들어가면 복잡도가 만배(…)