Issue: Windows 환경에서의 prefork와 solo, eventlet, gevent pool 방식

KimJiHong·2023년 12월 24일
1

Issue

목록 보기
1/7

Issue

나는 Celery를 통해서 비동기 분산 시스템 구현을 테스트하고 있는 도중
Mac 환경에서는 Celery -> Broker(RabbitMQ) -> worker 순으로 TASK를 정상적으로 받아
worker에서 TASK를 처리하는데,

Windows 환경에서는 동일하게 Celery -> Broker -> worker 순으로 TASK를 정상적으로 받지만
worker에서 child process calling 즉, 처리할 프로세서를 호출만 하고 실제로 실행은 하지 않고 있다.


내가 Celery 테스트를 위해 구현한 TASK print_test는 아래와 같다.

# accounts/tasks.py

from celery import shared_task

@shared_task
def print_test():
    print("해치웠나?")

Celery-Worker의 [tasks]에도 정상적으로 등록이 되어있고, 애초에 이러한 오류라면 KeyError 가 발생해야 하는데, 이건 오류도 발생안하고 그냥 worker가 TASK 메시지를 받고 무시하고 있다.

Prefork problem.

나는 위 문제의 답을 Celery School 사이트와 다른 블로그에서 얻을 수 있었다.

Windows 환경에서 위와 같은 오류가 뜨는 이유는 Celery가 멈춘게 아닌 Prefork Pool 이 멈춘 것이다.

Prefork Pool?

Prefork PoolCelery의 default PoolUnix 기반 시스템에서 Parents Process가 Child Process 를 호출하는 방식이다. 즉, 부모의 리소스가 자식 프로세스에서 상속이 된다.
(Prefork Pool은 Celery의 default Pool이다.)

Celery에는 billiard 패키지로 Prefork 을 지원하지 않는 Window 환경에서는 Spawning으로, Unix 기반 운영체제에서는 Prefork를 선택했었다.
하지만, Celery Verion 4로 올라오면서 더이상 이런 기능을 지원하지 않게 되었다.

Spawning?

spawningParents Process가 독립적인 새로운 Child Process를 생성하는 것이다. 즉, 부모 프로세서와 자식 프로세서간의 연관이 없다.

그러면 Windows 환경에서 아예 돌릴 수는 없을까?

Solution

Solo Pool

첫번째 방법은 하나의 Pool만 사용하는 것이다. (한번에 한개의 작업만 처리하는 방법)

즉, worker와 동일한 프로세스에서 쓰레드를 실행하는 방법이다.

# Solo Pool

# solo Pool 방식은 concurrency 옵션을 줘도 의미가 없음.
celery {app} worker -l info -P solo 

Eventlet & Gevent Pool

Celery는 여러개의 작업을 처리하기 위해서 프로세서를 생성하는데, 일정 프로세서 수가 넘어가면 프로세스 관리하는 오버헤드가 더 커져서 비효율적이게 된다. (멀티 프로세스 < 오버헤드 시점.)

또, Network I/O의 경우에는 CPU 연산이 필요한게 아니라 응답이 올때까지 기다려야 하는 경우가 대부분이라 Prefork 방식으로 프로세스를 늘리면 오히려 오버헤드만 증가하게 된다.

따라서, 이러한 경우에는 프로세스를 늘리는 것보다 Thread를 늘리는게 효율적이다.

Celery에는 Thread 기반의 Pool인 EventletGevent Pool을 제공하는데, Prefork방식을 지원하지 않는 Windows 환경에서는 쓰레드 기반의 Pool로 worker를 실행시켜 해결할 수 있다.
(더 정확하게는 Thread 보다는 Greenlet을 사용한다고 한다.)

  • Eventlet Pool
    • Python의 가벼운 Thread인 Greenlet를 기반으로 한 라이브러리.
    • I/O 대기중 다른 작업을 처리할 수 있어 효과적이다.
    • 하지만 네트워크 부하가 매우 큰 곳에서는 성능 저하가 일어날 수 있다.
  • Gevent Pool
    • Libev, Libuv와 같은 event loop를 기반으로 한 라이브러리.
    • Greenlet을 사용해 비동기 작업을 처리함.
    • Eventlet와 달리 네트워크 부하가 큰 곳에서 효과적이다.
# Gevent Pool
celery {app} worker -l info -P gevent -c 8

# Eventlet Pool
celery {app} worker -l info -P Eventlet -c 8

concurrency 설정 (-c) TIP

concurrency 설정은 Prefork 방식에서 기본으로 CPU의 코어에 맞게 설정되지만 Thread 기반의 Pool을 사용할 때는 CPU의 코어와 관련이 없어 가능한 많이 적는 것이 좋다.
참고: https://postbarca.tistory.com/76

Ref.

https://celery.school/celery-on-windows
https://velog.io/@qlgks1/Django-Celery-Safety-Efficiency#5-gevent--eventlet
https://postbarca.tistory.com/76
https://bentist.tistory.com/55

profile
https://h0ng.dev

0개의 댓글

관련 채용 정보