source

Node.js에서 단일 스레드 비차단 IO 모델이 작동하는 방식

factcode 2023. 5. 24. 22:26
반응형

Node.js에서 단일 스레드 비차단 IO 모델이 작동하는 방식

저는 노드 프로그래머는 아니지만, 싱글 스레드 비차단 IO 모델이 어떻게 작동하는지 관심이 있습니다.노드-js-event-loop에 대한 이해라는 기사를 읽고 나니 정말 혼란스럽습니다.모델에 대한 예를 제시했습니다.

c.query(
   'SELECT SLEEP(20);',
   function (err, results, fields) {
     if (err) {
       throw err;
     }
     res.writeHead(200, {'Content-Type': 'text/html'});
     res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');
     c.end();
    }
);

Que: 스레드가 하나뿐이기 때문에 요청 A(먼저)와 요청 B가 두 개일 때, 서버 측 프로그램은 요청 A를 먼저 처리합니다: SQL 쿼리를 수행하는 것은 I/O 대기를 대기하는 대기 상태입니다.그리고 그 프로그램은 고정되어 있습니다.I/O웹 페이지를 렌더링하는 코드를 실행할 수 없습니다.대기 중에 프로그램이 B 요청으로 전환됩니까?단일 스레드 모델 때문에 한 요청을 다른 요청으로 전환할 방법이 없다고 생각합니다.하지만 예제 코드의 제목에는 당신의 코드를 제외한 모든 것이 병렬로 실행된다고 나와 있습니다.

(P.S 저는 Node를 사용한 적이 없기 때문에 코드를 잘못 이해하고 있는지 잘 모르겠습니다.)대기 중에 노드가 A를 B로 전환하는 방법은 무엇입니까?그리고 노드의 싱글스레드 논블로킹 IO 모델을 간단하게 설명할 수 있습니까?당신 도움을 받을 수 있다면 감사하죠.:)

Node.js는 지원되는 OS(Unix, OS X 및 Windows)에서 제공하는 비동기(비블로킹) 입력/출력에 대한 apis/syscall을 추상화하는 교차 플랫폼 라이브러리인 libuv를 기반으로 합니다.

비동기 IO

이 프로그래밍 모델에서 파일 시스템이 관리하는 장치 및 리소스(소켓, 파일 시스템 등)에 대한 열기/읽기/쓰기 작업은 호출 스레드(일반적인 동기식 c-like 모델에서처럼)를 차단하지 않고 새 데이터나 이벤트가 사용 가능할 때 알림을 받을 프로세스(커널/OS 수준 데이터 구조)를 표시하기만 합니다.웹 서버와 유사한 앱의 경우, 프로세스는 통지된 이벤트가 어떤 요청/컨텍스트에 속하는지 파악하고 거기서 요청을 처리합니다.이는 단일 스레드 프로세스가 새로운 이벤트를 처리하기 위해 프로세스의 디스패처에 양보해야 했기 때문에 OS에 대한 요청을 시작한 스택 프레임과 다른 스택 프레임에 있어야 한다는 것을 의미합니다.

제가 설명한 모델의 문제는 본질적으로 비순차적이기 때문에 프로그래머에게 친숙하지 않고 추론하기 어렵다는 것입니다."A 기능에서 요청을 하고 A의 현지인이 일반적으로 사용할 수 없는 다른 기능에서 결과를 처리해야 합니다."

노드 모델(계속 전달 스타일 및 이벤트 루프)

노드는 프로그래머가 특정 프로그래밍 스타일을 사용하도록 유도함으로써 이 모델을 조금 더 동기적으로 보이게 하기 위해 자바스크립트의 언어 기능을 활용하여 문제를 해결합니다.에는 IO와 같은 .function (... parameters ..., callback)요청된 작업이 완료될 때 호출될 콜백을 제공해야 합니다(대부분의 시간은 OS가 완료 신호를 보낼 때까지 대기하는 데 소요됨 - 다른 작업을 수행하는 데 소요될 수 있습니다.Javascript의 Closure 지원을 통해 콜백의 본문 안에 있는 외부(호출) 함수에서 정의한 변수를 사용할 수 있습니다. 이를 통해 노드 런타임에 의해 독립적으로 호출될 다른 함수 간의 상태를 유지할 수 있습니다.계속 전달 스타일도 참조하십시오.

게다가, IO 연산을 생성하는 함수를 호출한 후에 호출하는 함수는 일반적으로return노드의 이벤트 루프에 대한 제어.이 루프는 실행이 예약된 다음 콜백 또는 함수를 호출합니다(해당 이벤트가 OS에 의해 통지되었기 때문일 가능성이 높습니다). 이렇게 하면 여러 요청을 동시에 처리할 수 있습니다.

노드의 이벤트 루프는 커널의 디스패처와 다소 유사하다고 생각할 수 있습니다. 커널은 보류 중인 IO가 완료되면 차단된 스레드 실행을 예약하고 노드는 해당 이벤트가 발생할 때 콜백을 예약합니다.

동시성이 뛰어나고 병렬 처리가 필요 없음

마지막으로,"당신의 코드를 제외한 모든 것이 병렬로 실행된다"는 문구는 당신의 코드가 하나의 스레드로 동시에 당신의 모든 js 로직을 다중화하고 시퀀싱함으로써 당신의 코드가 수십만 개의 열린 소켓으로부터의 요청을 처리할 수 있도록 하는 점을 포착하는 적절한 작업을 합니다.parallel"은 여기서 아마도 옳지 않을 것입니다 - 동시성병렬성 - 차이점은 무엇입니까?를 참조하십시오.대부분의 시간이 실제로 네트워크 또는 디스크(데이터베이스/소켓)를 기다리는 데 사용되고 논리적으로 CPU를 많이 사용하지 않기 때문에 웹 애플리케이션 서버에 적합합니다. 즉, IO 바인딩 워크로드에 적합합니다.

어떤 관점에서 node.js와 apache를 비교해 보겠습니다.

Apache는 다중 스레드 HTTP 서버이며, 서버가 수신하는 모든 요청에 대해 해당 요청을 처리하는 별도의 스레드를 만듭니다.

반면 Node.js는 이벤트 기반으로 단일 스레드에서 모든 요청을 비동기식으로 처리합니다.

Apache에서 A와 B가 수신되면 요청을 처리하는 두 개의 스레드가 생성됩니다.각 쿼리를 개별적으로 처리하고, 페이지를 제공하기 전에 쿼리 결과를 기다립니다.이 페이지는 쿼리가 완료될 때까지만 제공됩니다.서버가 결과를 수신할 때까지 나머지 스레드를 실행할 수 없으므로 쿼리 가져오기가 차단됩니다.

노드에서 c.query는 비동기적으로 처리됩니다. 즉, c.query가 A에 대한 결과를 가져오는 동안 B에 대한 c.query를 처리하기 위해 점프하고 결과가 A에 도착하면 결과를 콜백으로 다시 전송하여 응답을 보냅니다.Node.js는 가져오기가 완료되면 콜백을 실행하도록 알고 있습니다.

제 생각에는 단일 스레드 모델이기 때문에 한 요청에서 다른 요청으로 전환할 방법이 없습니다.

실제로 노드 서버는 항상 이 작업을 수행합니다.스위치(비동기 동작)를 만들기 위해 사용하는 대부분의 함수에는 콜백이 있습니다.

편집

SQL 쿼리는 mysql 라이브러리에서 가져옵니다.SQL 요청을 대기하는 이벤트 이미터와 콜백 스타일을 구현합니다.비차단 I/O 추상화를 제공하는 내부 libuv 스레드에 의해 수행되는 비동기식으로 실행되지 않습니다.쿼리를 만들 때 다음 단계가 수행됩니다.

  1. DB에 대한 연결을 열면 연결 자체가 비동기적으로 이루어질 수 있습니다.
  2. db가 연결되면 쿼리가 서버로 전달됩니다.쿼리를 대기열에 넣을 수 있습니다.
  3. 주 이벤트 루프는 콜백 또는 이벤트와 함께 완료되었음을 알립니다.
  4. 주 루프가 콜백/이벤트 처리기를 실행합니다.

http 서버에 대한 수신 요청도 유사한 방식으로 처리됩니다.내부 스레드 아키텍처는 다음과 같습니다.

node.js 이벤트 루프

C++ 스레드는 비동기 I/O(디스크 또는 네트워크)를 수행하는 lib 스레드입니다.기본 이벤트 루프는 스레드 풀로 요청을 발송한 후에도 계속 실행됩니다.대기 또는 절전 모드를 사용하지 않기 때문에 더 많은 요청을 수락할 수 있습니다. SQL 쿼리/HTTP 요청/파일 시스템 읽기는 모두 이러한 방식으로 이루어집니다.

Node.js는 뒤에서 libuv를 사용합니다.libuv에는 스레드 풀(기본적으로 크기 4)이 있습니다.따라서 Node.js는 스레드를 사용하여 동시성을 달성합니다.

그러나 코드는 단일 스레드에서 실행됩니다(즉, Node.js 함수의 모든 콜백은 동일한 스레드, 이른바 루프 스레드 또는 이벤트 루프에서 호출됩니다).사람들이 "Node.js는 단일 스레드에서 실행된다"고 말할 때 실제로 "Node.js의 콜백은 단일 스레드에서 실행된다"라고 말합니다.

Node.js는 이벤트 루프 프로그래밍 모델을 기반으로 합니다.이벤트 루프는 단일 스레드에서 실행되고 이벤트를 반복적으로 기다린 다음 해당 이벤트에 가입된 이벤트 핸들러를 실행합니다.이벤트는 예를 들어 다음과 같습니다.

  • 타이머 대기가 완료되었습니다.
  • 다음 데이터 청크를 이 파일에 쓸 준비가 되었습니다.
  • 새로운 HTTP 요청이 우리에게 오고 있습니다.

이 모든 것은 단일 스레드에서 실행되며 자바스크립트 코드는 병렬로 실행되지 않습니다.이러한 이벤트 핸들러가 작고 더 많은 이벤트 자체를 기다리는 한 모든 것이 잘 해결됩니다.이를 통해 단일 Node.js 프로세스에서 여러 요청을 동시에 처리할 수 있습니다.

(사건이 발생하는 곳에는 후드 아래에 약간의 마법이 있습니다.그 중 일부는 병렬로 실행되는 낮은 수준의 작업자 스레드를 포함합니다.)

이 SQL의 경우 데이터베이스 쿼리를 만드는 것과 콜백에서 결과를 얻는사이에는 많은(이벤트)이 발생합니다.이 시간 동안 이벤트 루프는 애플리케이션에 지속적으로 생명을 불어넣고 다른 요청을 한 번에 하나의 작은 이벤트로 진행합니다.따라서 여러 요청이 동시에 처리되고 있습니다.

이벤트 루프 상위 수준 보기

다음에 따르면: "Node.js 뒤의 10,000ft - 코어 개념의 이벤트 루프".

c.query() 함수에는 두 개의 인수가 있습니다.

c.query("Fetch Data", "Post-Processing of Data")

이 경우 "데이터 가져오기" 작업은 DB-Query이며, 이제 작업자 스레드를 분사하고 DB-Query를 수행하는 이 작업을 Node.js에 의해 처리될 수 있습니다(Node.js는 내부적으로 스레드를 생성할 수 있음을 기억하십시오).이를 통해 기능이 지연 없이 즉시 복귀할 수 있습니다.

두 번째 인수 "Post-Processing of Data"는 콜백 함수이며, 노드 프레임워크는 이 콜백을 등록하고 이벤트 루프에 의해 호출됩니다.

그므로그진은라는 c.query (paramenter1, parameter2)즉시 반환되어 노드가 다른 요청을 처리할 수 있습니다.

추신: 저는 이제 막 노드를 이해하기 시작했습니다. 사실 저는 이 글을 @Philip에게 댓글로 쓰고 싶었지만, 충분한 평판 포인트가 없었기 때문에 답변으로 썼습니다.

좀 더 자세히 읽어보면 "물론 백엔드에는 DB 액세스 및 프로세스 실행을 위한 스레드와 프로세스가 있습니다.그러나 이러한 스레드는 코드에 명시적으로 노출되지 않으므로 데이터베이스와 같은 I/O 상호 작용 또는 각 요청의 관점에서 비동기 프로세스가 발생한다는 사실을 알고 있는 것 외에는 걱정할 필요가 없습니다. 이러한 스레드의 결과는 이벤트 루프를 통해 코드로 반환되기 때문입니다."

"코드를 제외한 모든 것이 병렬로 실행됩니다." - 코드가 동기적으로 실행됩니다. IO 대기와 같은 비동기 작업을 호출할 때마다 이벤트 루프가 모든 것을 처리하고 콜백을 호출합니다.당신이 생각할 필요가 없는 것입니다.

예를 들어, 두 개의 요청 A(먼저 온다)와 B가 있습니다. 요청 A를 실행하면 코드가 계속 동기화되어 실행되고 요청 B가 실행됩니다. 이벤트 루프는 요청 A를 처리합니다. 이벤트 루프가 완료되면 요청 A의 콜백을 결과와 함께 호출합니다.

좋아요, 지금까지는 대부분 확실해요까다로운 부분은 SQL입니다. SQL 실행 전체가 다른 스레드나 프로세스에서 실제로 실행되지 않는 경우 SQL 실행은 (비동기 실행용으로 만들어진 SQL 프로세서에 의해!) 개별 단계로 분해되어야 합니다. 여기서 비차단 단계가 실행되고 차단 단계(예:sleep)은 실제로 커널로 전송될 수 있으며(알람 인터럽트/이벤트로) 메인 루프의 이벤트 목록에 추가됩니다.

즉, SQL 등의 해석은 즉시 수행되지만 대기 중(커널에 의해 kqueue, epoll, ... 구조, 다른 IO 작업과 함께 향후 발생할 이벤트로 저장됨)에는 메인 루프가 다른 작업을 수행하고 최종적으로 해당 IO에 문제가 발생했는지 확인할 수 있습니다.

다시 한 번 말씀드리면, 프로그램이 정체되지 않고(허용되지 않음), 절전 호출이 실행되지 않습니다.이들의 임무는 커널(무엇인가를 쓰고, 무엇인가가 네트워크를 통해 오기를 기다리며, 시간이 경과하기를 기다립니다)이나 다른 스레드 또는 프로세스에 의해 수행됩니다.노드 프로세스는 각 이벤트 루프 주기에 한 번씩 OS에 대한 유일한 차단 호출에서 해당 작업 중 적어도 하나가 커널에 의해 완료되었는지 확인합니다.차단하지 않는 모든 작업이 완료되면 그 지점에 도달합니다.

알겠어요? :-)

난 노드를 모릅니다.그런데 c.query는 어디서 오는 겁니까?

event loop는 Node.js가 가능할 때마다 시스템 커널로 작업을 오프로드하여(JavaScript가 단일 스레드임에도 불구하고) 차단되지 않는 I/O 작업을 수행할 수 있도록 합니다.을 생각하다event loop지배인으로서

  • 되고 새요은고사감용다니에 됩니다.synchronous event demultiplexer보시다시피 각 작업 처리기도 등록되어 있습니다.

여기에 이미지 설명 입력

  • 그런 다음 이러한 요청이 실행되도록 동기화하여 스레드 풀(Worker Pool)로 전송됩니다.JavaScript가 비동기 I/O 작업을 수행할 수 없습니다.브라우저 환경에서 브라우저는 비동기 작업을 처리합니다.환경에서 은 를 됩니다.libuv을 이용하여C++Thread의4이지만 시 설정을 할 수 .UV_THREADPOOL_SIZE크기 4는 한 번에 4개할 수 을 의미합니다. 요청이 입니다.스레드 풀 크기 4는 한 번에 4개의 요청을 실행할 수 있음을 의미합니다. 이벤트 디멀티플렉서에 5개의 요청이 있는 경우 4개는 스레드 풀로 전달되고 5번째는 대기 중입니다.각 요청이 실행되면 결과는 '이벤트 디멀티플렉서로 반환됩니다.

여기에 이미지 설명 입력

  • 일련의 I/O 작업이 완료되면 Event Demultiplexer는 해당하는 일련의 이벤트를 Event Queue로 푸시합니다.

여기에 이미지 설명 입력

핸들러가 콜백입니다.이제 이벤트 루프는 이벤트 대기열을 주시하고, 준비된 것이 있으면 콜백을 실행하기 위해 스택으로 푸시됩니다.콜백은 결국 스택에서 실행됩니다.일부 콜백은 다른 콜백에 우선 순위가 있지만 이벤트 루프는 우선 순위에 따라 콜백을 선택합니다.

짧은 답변을 원하지만 Node.js 내부의 가장 깊은 수준으로 가고 싶지 않은 사람들을 위한 것입니다.

Node.js는 단일 스레드가 아니며 기본적으로 5개 스레드에서 실행됩니다.

네, 유일한 스레드는 실제 자바스크립트 처리를 위한 것이지만 항상 함수에서 함수로 전환됩니다.

단일 스레드 Node.js가 계산 준비가 된 다른 코드를 계속 계산하는 동안 SQL 쿼리를 데이터베이스로 전송하여 다른 스레드에서 대기하도록 합니다.

더 많은 설명을 원하시면 이벤트 루프, 작업자 풀 및 전체 lib 설명서대한 좋은 기사가 있습니다.

언급URL : https://stackoverflow.com/questions/14795145/how-the-single-threaded-non-blocking-io-model-works-in-node-js

반응형