Webflux, Nodejs, Nginx에 어떻게 적은 Thread로 동작할까?

Moondy·2022년 6월 10일
0

SpringMVC - Tomcat는 어떻게 동작하나?

  • SpringMVC에서 많이 사용하는 WAS는 Tomcat → 쓰레드 풀의 개수가 200개 이상
  • 동작 방법
    • 요청이 들어오면 ThreadPool에서 Thread를 하나 사용
    • 그러나 I/O가 발생하면 CPU를 block 시킴
    • 이 때 다른 요청이 들어오면 ThreadPool에서 Thread를 하나 사용
    • 이런 식으로 쓰레드를 돌아가면서 요청 처리하고, block이 풀리면 작업을 이어나감
  • 결론
    • Thread가 엄청 많이 필요하다
    • 쓰레드가 많으면 쓰레드 사이에서 공유 자원의 동기화 이슈가 생긴다
    • Lock 하고 있는 자원에 접근하려는 쓰레드는 기다려야 해서 처리가 느리다

💡 컨텍스트 스위칭: 실행중인 프로세스가 변경이 되면 CPU내 레지스터의 값이 변경되어야 하는데, 변경 되기 전에 이전 프로세스가 지니고 있던 데이터들을 어딘가에 저장해주어야 한다. 그러다 다시 이전 프로세스를 이어가려면 값을 불러와야한다. 이 과정에서 시스템에 부담을 많이 준다.

스레드는 공유하는 메모리 영역이 많기 때문에 프로세스보다 비교적 컨텍스트 스위칭이 빠르긴 하지만, 그래도 여전히 부담을 준다.

WebFlux는 어떻게 적은 쓰레드 만으로 많은 요청을 처리할 수 있을까?

  • 적은 쓰레드로 처리 (core*2)
  • 쓰레드 구성
    • 요청을 받는 쓰레드(A)
    • block 상태에서 풀린 쓰레드의 요청을 처리하는 쓰레드(B)
    • block 상태가 풀렸는지 무한 루프 돎녀서 감시하는 event loop를 위한 쓰레드(C)
  • 동작 방법
    • 요청이 들어오면 A 쓰레드에서 처리
    • 그러다 I/O 발생시 CPU block시킴
    • 이런 비동기 작업을 처리하기 위해 Queue에 넣음
    • A쓰레드는 계속해서 요청을 받아 처리
    • C는 Queue를 무한뤁프 돌면서 감시
    • C에서 감시를 하다가 작업이 끝난 이벤트가 있으면 B 쓰레드에서 해당 이벤트를 처리
  • 결론
    • MQ처럼 이벤트 기반으로 비동기 처리하는 개념인 것 같다
    • 쓰레드가 적어 공유 자원의 동기화 이슈가 적게 발생해 더 빠르다

NodeJS에서는 어떻게 싱글 쓰레드로 동작할까?

  • NodeJS도 비동기 처리
  • 구성
    • 이벤트 루프: 이벤트 발생시 콜 스택을 확인하고 콜 스택이 비어있는 경우에만 테스크 큐에 콜백 함수를 넘겨줌
    • 테스크 큐: web api에서 비동기 작업들이 실행된 후 호출되는 콜백 함수들이 기다리는 공간. FIFO 처리
    • web api: 브라우저에서 지원하는 api로 DOM이벤트 Ajax 등 비동기 작업을 수행할 수 있도록 지원
  • 동작 방법
    • 이벤트 발생
    • 콜 스택에 쌓이게 됨
    • web api가 비동기 작업 수행
    • 콜백 함수를 이벤트 루프를 통해 테스크 큐에 넘겨주게 됨
    • 이벤트 루브는 콜 스택에 쌓여있는 함수가 없을 때 테스크 큐에서 대기하고 있던 콜백 함수를 콜 스택에 넘겨줌
    • 콜 스택에 쌓인 콜백함수가 실행된 후 스택에서 제거됨
  • 결론
    • 싱글 스레드지만 비동기 I/O 작업을 통해 요청들을 서로 블로킹 하지 않음
    • 동시에 많은 요청을 비동기로 수행함으로 써 싱글스레드 일지라도 논 블로킹 가능
  • 완전한 싱글 스레드는 아니다
    • NodeJS는 싱글 스레드이지만 완전한 싱글스레드를 기반으로 동작하지는 않는다
    • 일부 블로킹 작업들은 libuv 스레드 풀에서 수행됨
    • 자세한 내용은 하단의 링크 참고

NginX

  • 개념
    • WAS 중 하나
    • Event-Driven한 방식으로 한개의 고정된 프로세스만 생성
    • 요청에 대한 처리는 스레드에 의존하지 않고 프로세스 내부에서 비동기 방식으로 처리
    • 동시 접속 요청이 많아도 비동기 처리로 인해 스레드 생성 비용 따로 들지 않는다
    • 문제점
      • Event 처리 기간이 길어져 단일 Thread를 점유하고 있으면 다른 Event처리도 느려지는 Blocking 현상
    • 해결
      • NginX Thread Pool 도입 (1.7.11 버전 이후)
        • worker precess가 긴 작업인 경우 직접 실행하지 않고 task queue 작업에 넘김
        • task queue는 thread pool에서 작업을 수행할 수 있는 스레드에게 작업을 주면서 풀 관리
        • MultiThread 가능하면서, 하나의 thread가 blocking 되어도 나머지 thread가 작동하는데 문제 없다.

내 의견

  • 동기화 이슈를 좀 덜 겪으려면 스레드가 적을수록 좋은 것 같고, 스레드 생성 비용도 크기 때문에 점점 적은 쓰레드로 경량화 하는게 트렌드 인가보다
  • 이를 위해서는 task를 queue에 쌓아서 해결하는 비동기 + Event-driven 방식을 주로 도입하는 추세인 듯 하다

profile
DevOps를 살짝 찍먹하는 BackEnd 개발자

0개의 댓글