본문 바로가기

Web

django + celery + RabbitMQ 적용해서 비동기 task queuing하기.


Django로 웹 서비스를 개발하다보면, 백엔드(Backend)에서 어떤 프로세싱을 해야할 일이 생길것이다. 예를들면 업로드된 영상을 처리한다던지, 어떤 결제나 승인을 대기한다던지등등.. 이런 작업을 처리하기 위한 시스템을 만들기 위해서 Celery라는 분산 메세지 패싱기반 비동기 태스크 큐(Queue)를 사용하면 좋다.


나도 처음 써봤는데.. 아직 전체 구조가 확실히 눈에 들어오지는 않지만, 간단한 예제를 돌려보니 대충 감은 온다.


구글링을 해보면 참 설명이 많이 나오는데.. 이게 Celery 버젼에 따라 설정방법이 바뀐것도 있고해서.. 몇번 시도하다가 에러만 계속 나길래.. 이것저것 건들여보다가 정상동작 하는것을 확인하고 블로그에 기록을 해둔다.


1. Celery 3.0 설치


웹사이트 : http://www.celeryproject.org/


위 사이트에 방문해서 최신 버젼인 3.0버젼을 다운로드받아 설치하거나, 간단하게 


$ sudo pip install celery django-celery


로 설치하면 되겠다.django-celery는 django에서 사용하는 celery client라고 한다.


<Celery 공식 웹사이트>


2. RabbitMQ 서버 설치


$ sudo apt-get install rabbitmq-server


RabbitMQ의 기능과 역할에 대해서는 http://www.rabbitmq.com/ 에서 내용을 확인하자. 본인도 정확히는 모르겠지만, 어플리케이션 간에 메세지를 패싱(송수신..)하는 시스템인것 같다. 일단깔자. RabbitMQ가 Celery를 사용하기위한 메세지 브로커 역할을 하는데, RabbitMQ가 Celery의 default broker로 설정되어 있다고 한다.


3. Django 에서 설정


settings.py에 아래 내용을 추가한다.


import djcelery

djcelery.setup_loader()


BROKER_URL = "amqp://guest:guest@localhost:5672//"


구글링을 해보면, BROKER_HOST, PORT, USERNAME.. 뭐 이렇게 설정하라고 되어있는데, 그런 설정은 더이상 지원하지 않으니, celery 3.0을 사용한다면 위 처럼 BROKER_URL에서만 설정해주면 된다. amqp는 RabbitMQ를 메세지 브로커로 사용하기 때문이고, 첫번째 guest는 rabbitmq 서버의 username이고 두번째 guest는 비밀번호이다. 물론 사용자명과 패스워드는 바꿀수 있다.


$sudo rabbitmqctl add_user <username> <password>


로 추가해주면된다.


삭제는


$ sudo rabbitmqctl delete_user <username> 


해주면 된다. < >괄호내에는 추가하거나 삭제할 id와 password를 넣어주면 되겠다.


혹시나해서 적어두는데, settings.py 파일에 


INSTALLED_APPS = ( 

'djcelery',

'myapp',

)


두가지를 추가해야 한다. myapp은 개발하고 있는 app의 이름이 되겠다.


4. 태스크 만들기


이제 django에서 새로운 app을 개발하고 있다면, 그 app에서 tasks.py 파일을 만들고..


from djcelery import celery


@celery.task(name='tasks.add')

def add(x,y):

  return x + y


@celery.task(name='tasks.sleeptask')

def sleeptask(i):

  from time import sleep

  sleep(i)

  return i


를 추가해준다. 테스트 목적으로 두개의 태스크를 만들었다. 하나는 즉시 값을 리턴하는 add함수와 10초뒤에 태스크를 리턴하는 sleeptask 함수이다.


5. View 만들기


이제 태스크를 동작시킬 view를 만들자.


django app의 views.py에 아래의 내용을 추가한다.



from django.http import HttpResponse

from myapp import tasks


def test_celery(request):

    result = tasks.sleeptask.delay(2)

    result2 = tasks.add.delay(2,5)

    return HttpResponse("this is task test (id : %s)"%result.id)


이렇게 view를 만들어놓고 url에 이 view를 호출할 수 있도록 해야한다.


urlpattern에 아래 내용을 추가해준다.


import views as taskview


urlpatterns = patterns(

                       ...

                       url(r'^test$',taskview.test_celery),

                       )


이제 모든것이 끝났다.


6. 테스트 하기


터미널을 하나 연다. 그리고, rabbitmq 서버를 실행시켜야 한다.


$ sudo rabbimq-server


실행하면 본인의 머신에서는 이렇게 뜬다.




그런다음에는, 다른 터미널에서는 celery를 실행시킨다. 작업하고 있는 django 프로젝트에 포지션을 이동하고.


$ python manage.py celeryd -l info


이러면 아래와 같이 뜬다.




그런뒤에, 다른 터미널을 또 열어서 django 서버를 실행시키면 된다.


$ python manage.py runserver


서버를 실행시키고, 접속해보면. 이렇게 뜰것이다.



위에 id는 task 결과 해싱된 id이다.


테스트는 ubuntu 12.10 64bit + django 1.5.1 + celery 3.0 + rabbitmq 2.8.4 이다.


직적해보면 알겠지만, django 서버를 실행시키고 웹브라우저에서 F5를 여러번 눌러서 view 호출을 하면, 큐에 작업이 F5를 누른만큼 등록된다. 그러면 처리된 결과가 콘솔에 누른 순서대로 보여진다. 이런 상황을 생각해보면, view에서 아무런 대책없이 이런 예제처럼 서비스가 만들어진다면, 큐에 쌓인 중복 태스크때문에 서버에 부하가 커질것이라 본다. 이 예의 방법말고 다른 방법으로 만들어야 할것같다.