일반적으로 Node.js는 10,000 개의 동시 요청을 어떻게 처리합니까?


394

Node.js는 단일 스레드와 이벤트 루프를 사용하여 한 번에 하나씩 만 요청을 처리합니다 (비 차단). 그러나 여전히 그 작동 방식은 10,000 개의 동시 요청을 말합니다. 이벤트 루프는 모든 요청을 처리합니까? 너무 오래 걸리지 않습니까?

멀티 스레드 웹 서버보다 속도가 빠른 방법을 이해할 수는 없습니다. 멀티 스레드 웹 서버는 리소스 (메모리, CPU)가 비싸지 만 여전히 더 빠르지 않다는 것을 알고 있습니다. 아마 틀렸을 것이다. 이 단일 스레드가 많은 요청에서 더 빠르며 10,000과 같은 많은 요청을 처리 할 때 일반적으로 수행하는 작업 (높은 수준)을 설명하십시오.

또한, 단일 스레드가 그 대량으로 잘 확장됩니까? Node.js를 배우기 시작했습니다.


5
대부분의 작업 (데이터 이동)은 CPU와 관련이 없기 때문에.
OrangeDog

5
Javascript를 실행하는 스레드가 하나만 있다고해서 다른 스레드가 많지 않다는 것을 의미하지는 않습니다.
OrangeDog

이 질문은 너무 광범위하거나 다른 여러 질문과 중복됩니다.
OrangeDog


단일 스레딩과 함께 Node.js는 "비 차단 I / O"라고합니다. 여기 모든 마법이 이루어집니다
Anand N

답변:


762

이 질문을해야한다면 대부분의 웹 응용 프로그램 / 서비스가하는 일에 익숙하지 않을 것입니다. 모든 소프트웨어가 다음을 수행한다고 생각할 것입니다.

user do an action
       
       v
 application start processing action
   └──> loop ...
          └──> busy processing
 end loop
   └──> send result to user

그러나 이것은 웹 애플리케이션 또는 실제로 데이터베이스를 백엔드로 사용하는 애플리케이션이 작동하는 방식이 아닙니다. 웹 앱은 다음을 수행합니다.

user do an action
       
       v
 application start processing action
   └──> make database request
          └──> do nothing until request completes
 request complete
   └──> send result to user

이 시나리오에서 소프트웨어는 데이터베이스가 돌아 오기를 기다리는 0 % CPU 시간을 사용하여 대부분의 실행 시간을 소비합니다.

멀티 스레드 네트워크 앱 :

멀티 스레드 네트워크 앱은 다음과 같이 위의 작업을 처리합니다.

request ──> spawn thread
              └──> wait for database request
                     └──> answer request
request ──> spawn thread
              └──> wait for database request
                     └──> answer request
request ──> spawn thread
              └──> wait for database request
                     └──> answer request

따라서 스레드는 데이터베이스가 데이터를 반환하기를 기다리는 0 % CPU를 사용하여 대부분의 시간을 보냅니다. 그렇게하는 동안 스레드마다 필요한 별도의 프로그램 스택을 포함하는 스레드에 필요한 메모리를 할당해야했습니다. 또한 전체 프로세스를 시작하는 것만 큼 비싸지 않은 스레드를 시작해야합니다. 싼.

단일 스레드 이벤트 루프

대부분의 시간을 0 % CPU를 사용하므로 CPU를 사용하지 않을 때 일부 코드를 실행하지 않는 이유는 무엇입니까? 이런 식으로 각 요청은 여전히 ​​멀티 스레드 응용 프로그램과 동일한 양의 CPU 시간을 갖지만 스레드를 시작할 필요는 없습니다. 그래서 우리는 이것을합니다 :

request ──> make database request
request ──> make database request
request ──> make database request
database request complete ──> send response
database request complete ──> send response
database request complete ──> send response

실제로 두 접근 방식은 처리를 지배하는 데이터베이스 응답 시간이기 때문에 거의 동일한 대기 시간으로 데이터를 반환합니다.

여기서 가장 큰 장점은 새로운 스레드를 생성 할 필요가 없어서 많은 속도를 낼 수있는 malloc을 많이 할 필요가 없다는 것입니다.

마술, 보이지 않는 스레딩

겉보기에 신비한 것은 위의 두 가지 접근 방식이 모두 "병렬"로 워크로드를 관리하는 방법입니다. 답은 데이터베이스가 스레드된다는 것입니다. 따라서 단일 스레드 앱은 실제로 다른 프로세스의 다중 스레드 동작 인 데이터베이스를 활용합니다.

단일 스레드 접근 방식이 실패하는 경우

데이터를 반환하기 전에 많은 CPU 계산을 수행해야하는 경우 단일 스레드 앱이 크게 실패합니다. 이제는 데이터베이스 결과를 처리하는 for 루프를 의미하지 않습니다. 그것은 여전히 ​​대부분 O (n)입니다. 내 말은 푸리에 변환 (예 : mp3 인코딩), 광선 추적 (3D 렌더링) 등을 수행하는 것입니다.

단일 스레드 응용 프로그램의 또 다른 함정은 단일 CPU 코어 만 사용한다는 것입니다. 따라서 쿼드 코어 서버가 있다면 (현재는 드문 일이 아닙니다) 다른 3 개의 코어를 사용하지 않는 것입니다.

멀티 스레드 접근이 실패하는 경우

스레드 당 많은 RAM을 할당해야하는 경우 멀티 스레드 앱이 크게 실패합니다. 첫째, RAM 사용량 자체는 단일 스레드 앱만큼 많은 요청을 처리 할 수 ​​없음을 의미합니다. 더구나, malloc은 느리다. 많은 웹 객체에 공통 인 많은 객체를 할당하면 단일 스레드 앱보다 속도가 느려질 수 있습니다. 이것은 node.js가 일반적으로이기는 곳입니다.

다중 스레드를 악화시키는 사용 사례 중 하나는 스레드에서 다른 스크립팅 언어를 실행해야하는 경우입니다. 먼저 일반적으로 해당 언어의 전체 런타임을 malloc해야합니다. 그런 다음 스크립트에서 사용하는 변수를 malloc해야합니다.

따라서 C 또는 go 또는 java로 네트워크 응용 프로그램을 작성하는 경우 스레딩 오버 헤드가 일반적으로 그리 나쁘지 않습니다. PHP 또는 Ruby를 제공하기 위해 C 웹 서버를 작성하는 경우 Javascript 또는 Ruby 또는 Python으로 더 빠른 서버를 작성하는 것이 매우 쉽습니다.

하이브리드 접근법

일부 웹 서버는 하이브리드 방식을 사용합니다. 예를 들어 Nginx와 Apache2는 네트워크 처리 코드를 이벤트 루프의 스레드 풀로 구현합니다. 각 스레드는 이벤트 루프를 동시에 실행하여 단일 스레드 요청을 처리하지만 여러 스레드간에 요청이로드 밸런싱됩니다.

일부 단일 스레드 아키텍처도 하이브리드 방식을 사용합니다. 단일 프로세스에서 여러 스레드를 시작하는 대신 쿼드 코어 시스템에서 4 개의 node.js 서버와 같은 여러 애플리케이션을 시작할 수 있습니다. 그런 다음로드 밸런서를 사용하여 프로세스간에 작업 부하를 분산시킵니다.

사실상 두 가지 접근 방식은 기술적으로 동일한 미러 이미지입니다.


105
이것은 지금까지 읽은 노드에 대한 가장 좋은 설명입니다. 즉, "단일 스레드 응용 프로그램은 실제로 다른 프로세스의 다중 스레드 동작을 활용한다 : 데이터베이스를."일 한
kenobiwan

클라이언트가 노드에서 여러 요청을하는 경우 (예 : 이름 가져 오기 및 수정)이 작업이 서버로 푸시되어 많은 클라이언트가 매우 빠르게 처리하도록하는 경우는 어떻습니까? 그런 시나리오를 어떻게 처리 할 수 ​​있습니까?
Remario

3
@ CaspainCaldion 그것은 매우 빠르고 많은 고객들이 무엇을 의미하는지에 달려 있습니다. 마찬가지로 node.js는 초당 1000 개 이상의 요청을 처리 할 수 ​​있으며 속도는 네트워크 카드 속도로만 제한됩니다. 클라이언트가 동시에 연결된 것이 아니라 초당 1000 개의 요청입니다. 문제없이 10000 개의 동시 클라이언트를 처리 할 수 ​​있습니다. 실제 병목 현상은 네트워크 카드입니다.
slebetman

1
@ slebetman, 최고의 설명. 한 가지, 만약 내가 어떤 정보를 처리하고 그에 따라 결과를 제공하는 머신 러닝 알고리즘을 가지고 있다면, 다중 스레드 접근법 또는 단일 스레드를
사용해야합니까

5
@GaneshKarewad 알고리즘은 CPU를 사용하고 서비스 (데이터베이스, REST API 등)는 I / O를 사용합니다. AI가 js로 작성된 알고리즘 인 경우 다른 스레드 또는 프로세스에서 실행해야합니다. AI가 다른 컴퓨터 (Amazon, Google 또는 IBM AI 서비스 등)에서 실행되는 서비스 인 경우 단일 스레드 아키텍처를 사용하십시오.
slebetman

46

당신이 생각하는 것은 대부분의 처리가 노드 이벤트 루프에서 처리된다는 것입니다. 노드는 실제로 I / O 작업을 스레드로 팜합니다. I / O 작업은 일반적으로 CPU 작업보다 수십 배 더 오래 걸리므로 CPU가 대기하는 이유는 무엇입니까? 게다가 OS는 이미 I / O 작업을 잘 처리 할 수 ​​있습니다. 실제로 노드는 기다리지 않기 때문에 훨씬 더 높은 CPU 사용률을 달성합니다.

비 유적으로, NodeJS는 I / O 요리사가 부엌에서 주문을 준비하는 동안 고객 주문을받는 웨이터라고 생각하십시오. 다른 시스템에는 고객 주문을 받고 식사를 준비하며 테이블을 비운 후 다음 고객에게만 참석하는 여러 요리사가 있습니다.


5
식당 비유에 감사드립니다! 나는 유추와 실제 사례를 훨씬 쉽게 배울 수 있다는 것을 알았습니다.
LaVache

13

Node.js는 단일 스레드와 이벤트 루프를 사용하여 한 번에 하나씩 만 요청을 처리합니다 (비 차단).

나는 당신이 여기서 말한 것을 오해 할 수 있지만, "한 번에 하나"는 이벤트 기반 아키텍처를 완전히 이해하지 못하는 것처럼 들립니다.

"전통적인"(이벤트 중심이 아닌) 애플리케이션 아키텍처에서 프로세스는 무언가가 발생하기를 기다리는 데 많은 시간을 소비합니다. Node.js와 같은 이벤트 기반 아키텍처에서 프로세스는 기다릴뿐만 아니라 다른 작업을 수행 할 수 있습니다.

예를 들어, 클라이언트로부터 연결을 받고, 수락하고, 요청 헤더 (http의 경우)를 읽은 다음 요청에 대한 조치를 시작합니다. 요청 본문을 읽을 수 있습니다. 일반적으로 클라이언트로 일부 데이터를 다시 보냅니다 (이는 포인트를 설명하기 위해 의도적으로 절차를 단순화 한 것입니다).

이러한 각 단계에서 대부분의 시간은 다른 쪽 끝에서 일부 데이터가 도착하기를 기다리는 데 소비됩니다. 주 JS 스레드에서 처리하는 데 소요되는 실제 시간은 일반적으로 매우 적습니다.

I / O 객체 (예 : 네트워크 연결)의 상태가 처리가 필요하도록 변경되면 (예 : 소켓에서 데이터를 수신하거나 소켓을 쓸 수있는 등) 메인 Node.js JS 스레드가 목록과 함께 깨어납니다. 처리해야하는 품목

관련 데이터 구조를 찾고 해당 구조에서 이벤트를 발생시켜 콜백이 실행되고, 수신 데이터를 처리하거나, 소켓에 더 많은 데이터를 쓰는 등 처리가 필요한 모든 I / O 오브젝트가 있으면 처리되면 기본 Node.js JS 스레드는 더 많은 데이터를 사용할 수 있거나 다른 작업이 완료되었거나 시간 초과되었다는 메시지가 표시 될 때까지 다시 기다립니다.

다음에 깨어 났을 때 다른 I / O 객체를 처리해야 할 수도 있습니다 (예 : 다른 네트워크 연결). 매번 관련 콜백이 실행 된 다음 다른 일이 발생하기를 기다리면서 다시 절전 모드로 전환됩니다.

중요한 점은 다른 요청의 처리가 인터리브되고, 한 요청을 처음부터 끝까지 처리하지 않고 다음 요청으로 이동한다는 것입니다.

내 생각에, 이것의 주요 장점은 느린 요청 (예 : 2G 데이터 연결을 통해 휴대 전화 장치에 1MB의 응답 데이터를 보내려고하거나 실제로 느린 데이터베이스 쿼리를 수행하려고 함) ' t 더 빠른 것을 차단하십시오.

기존의 다중 스레드 웹 서버에서는 일반적으로 처리되는 각 요청에 대한 스레드가 있으며 완료 될 때까지 해당 요청 만 처리합니다. 느린 요청이 많으면 어떻게됩니까? 이러한 요청을 처리하는 데 많은 스레드가 걸리고 다른 요청 (매우 빨리 처리 될 수있는 매우 간단한 요청 일 수 있음)이 요청 뒤에 대기합니다.

Node.js 외에도 다른 이벤트 기반 시스템이 많이 있으며, 기존 모델과 유사한 장단점이 있습니다.

이벤트 기반 시스템이 모든 상황 또는 모든 워크로드에서 더 빠르다고 주장하지는 않습니다. CPU 바인딩 시스템에는 적합하지 않고 I / O 바인딩 워크로드에 적합합니다.


12

단일 스레드 이벤트 루프 모델 처리 단계 :

  • 클라이언트 웹 서버에 요청을 보냅니다.

  • Node JS Web Server는 내부적으로 제한된 스레드 풀을 유지 관리하여 클라이언트 요청에 서비스를 제공합니다.

  • Node JS Web Server는 이러한 요청을 수신하여 대기열에 넣습니다. "이벤트 큐"라고합니다.

  • Node JS Web Server에는 내부적으로 "이벤트 루프"라고하는 구성 요소가 있습니다. 이 이름을 가진 이유는 요청을 수신하고 처리하기 위해 무한 루프를 사용하기 때문입니다.

  • 이벤트 루프는 단일 스레드 만 사용합니다. Node JS Platform Processing Model의 핵심입니다.

  • 이벤트 루프는 모든 클라이언트 요청이 이벤트 큐에 있는지 확인합니다. 그렇지 않으면 들어오는 요청을 무기한 기다립니다.

  • 그렇다면, 이벤트 큐에서 하나의 클라이언트 요청을 선택하십시오.

    1. 클라이언트가 요청한 프로세스를 시작합니다
    2. 해당 클라이언트 요청에 블로킹 IO 작업이 필요하지 않은 경우 모든 것을 처리하고 응답을 준비한 후 클라이언트로 다시 보냅니다.
    3. 해당 클라이언트 요청에 데이터베이스, 파일 시스템, 외부 서비스와의 상호 작용과 같은 일부 차단 IO 작업이 필요한 경우 다른 접근 방식을 따릅니다.
  • 내부 스레드 풀에서 스레드 가용성 확인
  • 하나의 스레드를 선택하고이 클라이언트 요청을 해당 스레드에 지정하십시오.
  • 스레드는 요청을 처리하고 처리하며 IO 작업 차단을 수행하고 응답을 준비한 후 이벤트 루프로 다시 보냅니다.

    매우 친절이 던져 이동 더 설명 @Rambabu 포사에 의해 설명 링크


해당 블로그 게시물에 제공된 다이어그램이 잘못된 것 같습니다. 해당 기사에서 언급 한 내용이 완전히 올바르지 않습니다.
rranj

11

slebetman 답변 추가 : Node.JS10,000 개의 동시 요청을 처리 할 수 있다고하면 본질적으로 비 차단 요청입니다. 즉, 이러한 요청은 주로 데이터베이스 쿼리와 관련이 있습니다.

내부적으로 event loopNode.JS를 처리하는 중입니다 thread pool. 여기서 각 스레드는 a를 처리 non-blocking request하고 이벤트 루프는의 스레드 중 하나에 작업을 위임 한 후 계속해서 더 많은 요청을 수신합니다 thread pool. 스레드 중 하나가 작업을 완료하면 event loop일명 완료되었음을 알리는 신호를 보냅니다 callback. Event loop그런 다음이 콜백을 처리하고 응답을 다시 보냅니다.

NodeJS를 처음 사용 nextTick하는 경우 이벤트 루프가 내부적으로 작동하는 방식을 이해하려면 추가 정보 를 읽으십시오 . http://javascriptissexy.com 에서 블로그를 읽으면 JavaScript / NodeJS를 시작할 때 정말 도움이되었습니다.


2

에 추가 slebetman 코드를 실행하는 동안 무슨 일에 더 명확하게하기 위해의 대답.

nodeJs의 내부 스레드 풀에는 기본적으로 4 개의 스레드 만 있습니다. 그리고 전체 요청이 스레드 풀에서 새 스레드에 연결되는 것과는 다릅니다. 요청이 오래 실행되거나 db와 같은 무거운 작업이있을 때마다 요청의 전체 실행이 일반 요청과 동일하게 발생합니다 (차단 작업 없음). call, 파일 작업 또는 http 요청 태스크가 libuv에 의해 제공되는 내부 스레드 풀에 대기됩니다. 그리고 nodeJs는 기본적으로 내부 스레드 풀에 4 개의 스레드를 제공하므로 스레드가 해제 될 때까지 5 번째 또는 다음 동시 요청이 대기 할 때마다 대기하며 이러한 작업이 끝나면 콜백 큐로 푸시됩니다. 이벤트 루프에 의해 선택되어 응답을 다시 보냅니다.

이제 여기에는 단일 콜백 큐가 아니라 많은 큐가 있다는 또 다른 정보가 있습니다.

  1. NextTick 대기열
  2. 마이크로 작업 대기열
  3. 타이머 대기열
  4. IO 콜백 대기열 (요청, 파일 운영, DB 운영)
  5. IO 폴 큐
  6. 단계 큐 또는 SetImmediate 확인
  7. 처리기 큐 닫기

요청이 올 때마다 코드는이 콜백 순서대로 실행됩니다.

블로킹 요청이있을 때와는 다르며 새 스레드에 첨부됩니다. 기본적으로 4 개의 스레드 만 있습니다. 거기에 또 다른 대기열이 있습니다.

코드에서 파일 읽기와 같은 블로킹 프로세스가 발생할 때마다 스레드 풀에서 스레드를 사용하는 함수를 호출 한 다음 작업이 완료되면 콜백이 해당 큐로 전달 된 다음 순서대로 실행됩니다.

콜백 유형에 따라 모든 것이 대기열에 들어가고 위에서 언급 한 순서대로 처리됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.