개발하는 일상

python multiprocessing 으로 병렬 작업하기(Django 서버 동시에 여러개 켜기, run multiple Django server) 본문

개발 간단 팁

python multiprocessing 으로 병렬 작업하기(Django 서버 동시에 여러개 켜기, run multiple Django server)

롯데빙빙바 2020. 10. 19. 01:17

개발 배경은 제 글 Django로 채점 서버 만들기에 나와있습니다.

Multiprocessing

python 코드는 보통 동기적으로 작동하며, 코드 한 줄의 실행이 완료되어야 다음 코드를 실행합니다. 그럼 코드의 종료를 기다릴 수 없는 코드를 여러 번 실행해야 할 때는 어떻게 해야할까요? 예를 들어, 아래의 코드를 통해 Django 서버 여러 개를 켜려고 한다고 가정하겠습니다.

import os
for i in range(8000,8010):
  os.system(f'python manage.py runserver {i}')

os.system은 터미널에서 해당 명령어를 실행해주는 메소드입니다.

python manage.py runserver <PORT_NUM> 는 터미널에서 Django 서버를 켜는 명령어입니다.

언뜻 보기엔 8000번 포트부터 8009번 포트까지 10개의 서버가 같이 돌아갈 것 같지만, 실제로는 그렇지 않습니다. 8000번 서버가 켜지고, 터미널에서 해당 서버가 종료될 때까지 다음 서버는 켜지지 않습니다. 이런 상황에서 사용할 수 있는 것이 python의 multiprocessing 패키지입니다.

이 글에서는 제가 적용한 간단한 두 가지의 사용법을 소개하겠습니다. 더욱 자세한 내용은 공식문서를 참고하시길 바랍니다.

Process

from multiprocessing import Process
import os
# 중략
def start_server(student, port):
  # target django project 이동 후
  os.chdir(BASE_DIR / student)
  # 서버 실행
  os.system(f'python manage.py runserver {port}')

# students_with_port looks like [('홍길동', 8000), ('김철수', 8001), ...]
if __name__=='__main__':
  for student, port in students_with_port:
    proc = Process(target=start_server, args=(student,port))
    proc.start()

먼저 반복문을 이용한 형태의 multiprocessing입니다. Process 클래스를 통해 프로세스 인스턴스를 만들고, .start()를 통해 프로세스를 실행할 수 있습니다.

Process 인스턴스를 만들 때, target 키워드 인자에는 실행될 함수를 넣고, 만약 함수 실행 시 전달해야할 인자가 있다면 args 키워드 인자로 전달할 수 있습니다.

여기서 제가 Process 관련 코드에 __name__ 관련 조건문을 달아 두었는데, 윈도우에서 이 파일을 직접 실행할 경우, 이 조건문이 없다면 코드가 무한루프에 빠지게 됩니다. 관련 stackoverflow 질문 답변

Pool

from multiprocessing import Pool
import os
# 중략
def start_server(student_with_port):
  student, port = student_with_port
  # target django project 이동 후
  os.chdir(BASE_DIR / student)
  # 서버 실행
  os.system(f'python manage.py runserver {port}')

# students_with_port looks like [('홍길동', 8000), ('김철수', 8001), ...]
if __name__ == '__main__':
  pool = Pool(len(students_with_port))
  pool.map(start_server, students_with_port)

다음으로 Pool을 이용한 multiprocessing입니다. Process와 크게 다르진 않습니다.

먼저 Pool 인스턴스를 만들 때, 사용할 프로세스 수를 넘깁니다. 저의 경우에는 동시에 켜야할 서버의 수가 학생들의 수 만큼이었으므로, 해당 리스트를 활용하였습니다.

다음으로 .map을 통해 프로세스를 돌리게 되는데, 첫 번째 인자로 각 프로세스마다 실행할 함수, 두 번째 인자로 함수에 전달할 인자리스트를 넣어줍니다. 두 번째 인자로 전달된 리스트의 요소마다 프로세스가 실행되게 됩니다.

함수에 전달해야 될 인자가 여러 개인 경우, 어떻게 처리해야 할지 몰라서 저는 인자를 튜플로 묶어서 전달하고 함수안에서 풀어서 사용하는 방식을 택했습니다.

이 코드 역시 윈도우에서 실행될 경우, __name__ 관련 조건문이 필요합니다.

만약 아래 코드와 같이 함수에 리턴 값이 있다면, 내장 함수 map과 동일한 동작을 해줍니다. 즉, 해당 프로세스의 결과를 리스트에 담아 리턴하게 됩니다.

def foo(num):
  return num*num
numbers = [1, 2, 3, 4]
pool = Pool(4)
squares = pool.map(foo, numbers) 
print(squares) # [1, 4, 9, 16]
Comments