“장고(Django) 멀티스레드”와 “Node.js 싱글 스레드”

프레임워크 작동 원리를 이해하고, 서버 자원 활용률을 높이자!

SoniaComp
9 min readMay 4, 2021

🤔 이 글의 출발점

친구와 같이 스터디를 하다가, ‘장고가 멀티스레드인지, 싱글스레드인지’에 대한 얘기가 나왔다. 구글에 검색해보니, 명확한 공식문서가 존재하지 않아 한번 정리하면 좋을 것 같아서 글을 쓰게 됐다!
(혹시 수정이 필요한 부분이 있으면 알려주시면 감사하겠습니다! 😊)

목차

시작글: '동시성'에 대하여[장고(Django)]
- Django Application
- WSGI를 이용한 Multithread (+ WSGI와 PM2)
- Nginx+uWSGI로 concurrency 높이기
- 멀티스레딩과 GIL
[Node.js]
- Single Threaded Event Loop Model architecture
- pm2를 이용한 자원 활용률 높이기
[Appendix]
- Nginx
- pm2

시작글: ‘동시성’ 에 대하여

멀티 스레드, 싱글 스레드를 아는 게 왜 중요할까?
→ 멀티 스레드를 통해 시스템의 동시성(Concurrency)를 증가시킬 수 있다!
→ 시스템의 확장성(Scalability)

  • 동시성을 증가시켜 단일 머신에서 애플리케이션을 확장
    → 잠재적인 성능 향상
  • 서버에서 시스템의 실행 시간이나 지연시간이 감소
    → 확장성에 긍정적인 영향을 줌
  • 동시성은 컴퓨팅 자원 측면에서 머신 안에서 애플리케이션을 확장하는 가장 저렴한 방법이다.

[ 파이썬에서 동시성을 지원하는 방법 ]

  1. 멀티 스레딩: 스레드는 CPU가 수행할 수 있는 프로그래밍 명령의 가장 간단한 시퀀스. 프로그램 작업을 여러 스레드로 분산해 동시에 더 많은 작업을 실행하도록 하는 것. [threading]
  2. 멀티 프로세싱: 단일 프로세스 대신 여러 프로세스에서 실행하는 것. 메시지 전달과 공유 메모리 측면에서 멀티 스레딩보다 더 많은 오버헤드를 가짐. CPU 집약적인 계산을 많이 수행하는 프로그램에서는 멀티 프로세스가 유리하다. [multiprocessing]
  3. 비동기 처리: 시간 측면의 특정 작업 순서와 상관 없이 비동기적으로 수행된다. 비동기 처리는 작업 대기열에서 작업을 선택하고 나중에 실행하기 위해 작업을 스케줄링 한다. 대부분 단일 스레드에서 발생한다. [asyncio]

Django

Django Application

  • Django is a single-line program natively [Default: 싱글 스레드]
  • When the first request is not completed, the second request will be blocked, and the second request will be executed only when the first request is completed. [순차적 진행(비동기를 사용하지 않음)]

WSGI를 이용한 Multithread

  • WSGI(Web Server Gateway Interface): 파이썬 웹 어플리케이션 프레임워크와 웹 서버 사이의 표준 인터페이스 명세
  • WSGI 요청이 처리되는 방법
    1) 서버 측은 애플리케이션을 실행하고 환경과 콜백 함수를 함께 제공
    2) 애플리케이션은 요청을 처리하고 제공된 콜백함수를 이용해 서버에 응답 반환
  • WSGI는 서버, 애플리케이션 두 측면 모두에서 명세를 구현하는 미들웨어
    → WSGI가 제공하는 기능
    1) 서버에서 애플리케이션으로 하는 여러 요청의 로드 밸런싱
    2) 네트워크를 통한 요청과 응답의 전달을 통한 요청의 원격 처리
    3) 같은 프로세스 안의 멀티테넌시나 여러 서버 또는 애플리케이션의 공동 호스팅
    4) 다양한 애플리케이션 객체에 대한 요청의 URL 기반 라우팅
  • Concurrency는 Multi Thread를 통해 구현된다.
    → WSGI 가 실행
    → The official document explains that the server that comes with django is multi-threaded by default.

uWSGI로 concurrency 높이기

  • uWSGI defaults to single process and single thread.
  • uWSGI의 프로세스 수와 스레드 수를 복수개로 설정하여 Concurrency를 높일 수 있다.
uwsgi --http 0.0.0.0:8000 --file app.py --processes 4 --threads 2
  • WSGI 의 다른 인기있는 구현체는 gunicorn이 있다. Gunicorn은 러닝 커브가 낮기 때문에 많은 커스터마이즈가 필요없는 배포를 할 때 사용한다. 반면 uWSGI 는 Nginx 에서 기본적으로 지원하기도 하고, 커스터마이징할 수 있는 기능이 더 많다. 예를들어 uWSGI는 파이썬 virtualenv 기반 배포를 간단하게 만든다. 반면, Gunicorn은 virtualenv를 지원하지 않기 때문에, 가상 환경으로 배포해야 한다.

멀티스레딩과 GIL

  • Global Interpreter Lock: 파이썬에는 멀티스레드가 네이티브 바이트 코드를 즉시 실행하는 것을 방지하는 전역적인 잠금. 파이썬의 네이티브 구현인 CPython의 메모리 관리는 스레드 안정성이 없기 때문에 잠금이 필요.
  • 파이썬에서 효율적인 경우
    → I/O Bound Process: I/O 호출과 장시간 실행되는 동작은 GIL 외부에서 발생. 따라서 이미지 처리 같은 작업이나 I/O를 포함하는 경우 효율적
    (I/O subsystem의 속도에 의해 프로세스 진행율이 제한 되는 경우)
  • 파이썬에서 비효율적인 경우
    → CPU bound process: GIL 때문에 CPU에서 바이트 코드 연산을 동시에 수행할 수 없다. (CPU 속도에 의해 프로세스 진행율이 제한되는 경우)
    → 프로그램이 동시에 수행해야 하는 많은 바이트 코드 연산에 의존하는 경우
    → 프로그램이 단일 머신에서 여러 CPU 코어의 완전한 기능을 활용하기 위해 멀티 스레드를 사용하는 경우
  • Single Process and Multiple Threads: Multiple Core를 사용하지 못함
    → Single-process multi-threaded python applications can achieve concurrency, but there is no parallelism (동시성이지만 병렬성은 아님)
  • 멀티프로세스에서 각 프로세스는 각자의 Thread Lock을 가지고 있음
    → multi-process multi-GIL
  • 멀티 프로세스와 멀티코어 어플리케이션은 동시성과 병렬성을 가짐.

Node.js

Single Threaded Event Loop Model architecture

출처: Multi threading and multiple process in Node.js

Node.js is a single threaded language which in background uses multiple threads to execute asynchronous code.

Node.js는 싱글 스레드 기반이고, 여러 비동기 코드를 처리할 수 있는 멀티 스레드와 함께 구성된다.

  • 싱글 스레드 : 호출 스택 1개 →한번에 한 작업만 처리
  • 한 개의 호출 스택을 갖고 있는 자바스크립트의 실행을 빠르게 하는 방법
    → 동시성(Concurrency) & 이벤트 루프(Event Loop) [ 비동기 콜백 ]

pm2를 이용한 자원 활용률 높이기

  • Node.js 애플리케이션은 단일 CPU 코어에서 실행(싱글 스레드)
  • 멀티 코어를 활용하기 위한 방법
    → 클러스터(Cluster) 모듈을 통해 단일 프로세스를 멀티 프로세스(Worker)로 늘릴 수 있는 방법
    → 클러스터 모듈을 사용해서 마스터 프로세스에서 CPU 코어 수만큼 워커 프로세스를 생성해서 모든 코어를 사용하게끔 함

Appendix

Nginx

  • 웹 서버 소프트웨어
  • 가벼움과 높은 성능을 목표로 한다.
  • 웹 서버, 리버스 프록시 및 메일 프록시 기능을 가진다.
    [ L3, L4, L7 ]
  • Apache 의 안정성, 확장성, 호환성을 장점으로 들자면,
    Nginx 는 성능이 우세하다는 장점이 있습니다.
  • Nginx는 요청에 응답하기 위해 비동기 이벤트 기반 구조를 가진다.
    아파치 HTTP 서버의 스레드/프로세스 기반 구조를 가지는 것과는 대조적 → 서버에 많은 부하가 생길 경우의 성능을 예측하기 쉽게 해준다.
  • 설정 튜닝: Response, Request 데이터 양을 늘려줌

pm2

  • 클러스터(Cluster) 모듈을 통해 단일 프로세스를 멀티 프로세스(Worker)로 늘릴 때
    → CPU 개수만큼 워커 프로세스를 생성하고 마스터 프로세스와 워커 프로세스가 각각 수행해야 할 일들을 정리해서 구현해야 함
    → 예를 들어 워커 프로세스가 생성됐을 때 온라인 이벤트가 마스터 프로세스로 전달되면 어떻게 처리할지, 워커 프로세스가 메모리 제한선에 도달하거나 예상치 못한 오류로 종료되면서 종료(exit) 이벤트를 전달할 땐 어떻게 처리할지, 그리고 애플리케이션의 변경을 반영하기 위해 재시작해야 할 때 어떤 식으로 재시작을 처리할 지
  • PM2라는 Node.js의 프로세스 매니저가 자동으로 처리해줌
# clusterize an app to all CPU cores available: [라운드로빈 방식으로 동작]
pm2 start app.js -i 3
  • CLI에서 자체적인 모니터링 기능도 제공해준다

--

--