programing

Nodejs 이벤트 루프

yellowcard 2023. 8. 23. 21:42
반응형

Nodejs 이벤트 루프

nodejs 아키텍처에는 내부적으로 두 개의 이벤트 루프가 있습니까?

  • livev/libuv
  • v8 Javascript 이벤트 루프

I/O 요청 시 노드는 livev를 사용하는 이벤트를 통해 데이터 가용성을 알리는 요청을 liveio로 큐잉하고 마지막으로 콜백을 사용하는 v8 이벤트 루프에서 처리합니까?

기본적으로 livev와 liveio는 nodejs 아키텍처에 어떻게 통합됩니까?

nodejs 내부 아키텍처를 명확하게 파악할 수 있는 문서가 있습니까?

저는 개인적으로 node.js & v8의 소스 코드를 읽고 있습니다.

네이티브 모듈을 작성하기 위해 node.js 아키텍처를 이해하려고 했을 때 저도 당신과 비슷한 문제를 겪었습니다.

제가 여기에 게시하는 것은 node.js에 대한 저의 이해이며, 이 또한 약간 틀릴 수 있습니다.

  1. Libev는 실제로 node.js에서 내부적으로 실행되어 간단한 이벤트 루프 작업을 수행하는 이벤트 루프입니다.원래 *nix 시스템용으로 작성되었습니다.Libev는 프로세스를 실행하기 위해 간단하지만 최적화된 이벤트 루프를 제공합니다.리브에 대한 자세한 내용은 여기에서 확인하실 수 있습니다.

  2. LibEio는 입력 출력을 비동기적으로 수행하는 라이브러리입니다.파일 설명자, 데이터 핸들러, 소켓 등을 처리합니다.당신은 여기에서 그것에 대해 더 읽을 수 있습니다.

  3. LibUv는 libeio, libev, c-ares(DNS의 경우) 및 iocp(윈도우즈 비동기-io의 경우) 위에 있는 추상화 계층입니다.LibUv는 이벤트 풀의 모든 io 및 이벤트를 수행, 유지 및 관리합니다(libeio 스레드 풀의 경우).당신은 libUv에 대한 Ryan Dahl의 튜토리얼을 확인해야 합니다.libUv 자체가 어떻게 작동하는지에 대해 더 이해하기 시작하면 node.js가 libuv 및 v8의 상단에서 어떻게 작동하는지 이해할 수 있습니다.

자바스크립트 이벤트 루프만 이해하려면 이 비디오들을 보는 것을 고려해야 합니다.

libeio가 비동기 모듈을 생성하기 위해 node.js와 함께 사용되는 방식을 보려면 다음 예제를 참조해야 합니다.

기본적으로 node.js 내부에서 발생하는 것은 v8 루프가 C++ 모듈뿐만 아니라 모든 자바스크립트 부품을 실행하고 처리하는 것입니다. (공식 문서 node.js 자체는 단일 스레드입니다.)메인 스레드 외부에서 libev와 libeio가 스레드 풀에서 처리하고 liebev는 메인 루프와의 상호 작용을 제공합니다.제가 알기로는 node.js는 1개의 영구 이벤트 루프를 가지고 있습니다. 바로 v8 이벤트 루프입니다.C++ 비동기 작업을 처리하기 위해 [libeio & livev]를 통해 스레드 풀을 사용하고 있습니다.

예:

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

모든 모듈에 나타나는 것은 일반적으로 기능을 호출하는 것입니다.Task실풀에서.작업이 완료되면 다음과 같이 호출합니다.AfterTask메인 스레드에서 기능합니다.에 반에면.Eio_REQUEST스레드 풀과 주 스레드 간의 통신을 제공하는 구조/객체가 될 수 있는 요청 처리기입니다.

논의된 일부 실체(예: libev 등)는 시간이 꽤 지났다는 사실 때문에 관련성을 잃은 것으로 보이지만, 여전히 그 질문은 큰 잠재력을 가지고 있다고 생각합니다.

오늘날 노드의 맥락에서 추상적인 UNIX 환경에서 이벤트 기반 모델의 작업을 추상적인 예를 사용하여 설명하고자 합니다.

프로그램의 관점:

  • 스크립트 엔진이 스크립트 실행을 시작합니다.
  • CPU 바인딩 작업이 발생할 때마다 인라인(실제 시스템)으로 완전하게 실행됩니다.
  • I/O 바인딩 작업이 발생할 때마다 요청과 해당 완료 처리기가 '이벤트 시스템'(가상 시스템)에 등록됩니다.
  • 스크립트가 종료될 때까지 위와 동일한 방법으로 작업을 반복합니다.CPU 바인딩된 작업 - 인라인, I/O 바인딩된 작업을 실행하고 위와 같이 기계에 요청합니다.
  • I/O가 완료되면 수신기가 다시 호출됩니다.

위의 이벤트 기계를 libuv AKA 이벤트 루프 프레임워크라고 합니다.노드는 이 라이브러리를 활용하여 이벤트 기반 프로그래밍 모델을 구현합니다.

노드의 관점:

  • 런타임을 호스팅할 스레드가 하나 있어야 합니다.
  • 사용자 스크립트를 선택합니다.
  • 네이티브로 컴파일 [leverage v8]
  • 이진 파일을 로드하고 진입점으로 이동합니다.
  • 컴파일된 코드는 프로그래밍 프리미티브를 사용하여 CPU 바인딩 작업을 인라인으로 실행합니다.
  • 많은 I/O 및 타이머 관련 코드에 네이티브 랩이 있습니다.예를 들어 네트워크 I/O입니다.
  • 따라서 I/O 호출은 I/O 핸들과 완료 핸들을 인수로 전달하여 스크립트에서 C++ 브리지로 라우팅됩니다.
  • 네이티브 코드는 libuv 루프를 행사합니다.루프를 획득하고, I/O를 나타내는 낮은 수준의 이벤트를 큐에 넣으며, 네이티브 콜백 래퍼를 libuv 루프 구조로 가져옵니다.
  • 네이티브 코드가 스크립트로 돌아갑니다. 현재 I/O가 수행되지 않습니다!
  • 위의 항목은 모든 비 I/O 코드가 실행되고 모든 I/O 코드가 등록될 때까지 여러 번 반복됩니다.
  • 마지막으로, 시스템에 실행할 수 있는 것이 아무것도 없을 때, 노드는 libuv에게 컨트롤을 전달합니다.
  • libuv는 동작을 시작하고, 등록된 모든 이벤트를 선택하고, 운영 체제에 쿼리를 실행하여 작동 가능성을 얻습니다.
  • 비차단 모드에서 I/O 준비가 된 항목을 선택하고, I/O를 수행하고, 콜백을 실행합니다.차례차례로.
  • 아직 준비되지 않은 것(예: 소켓 판독, 다른 엔드포인트가 아직 아무것도 쓰지 않은 것)은 사용할 수 있을 때까지 OS를 통해 계속 조사됩니다.
  • 루프는 내부적으로 항상 증가하는 타이머를 유지합니다.응용 프로그램이 지연된 콜백을 요청할 때(예: setTimeout) 이 내부 타이머 값을 활용하여 콜백을 실행하기 위한 적절한 시간을 계산합니다.

대부분의 기능은 이러한 방식으로 제공되지만 일부(비동기화 버전) 파일 작업은 libuv에 잘 통합된 추가 스레드의 도움을 받아 수행됩니다.네트워크 I/O 작업은 데이터로 응답하는 다른 엔드포인트와 같은 외부 이벤트를 예상하여 대기할 수 있습니다.파일 작업에는 노드 자체의 작업이 필요합니다.예를 들어, 파일을 열고 fd가 데이터와 함께 준비될 때까지 기다리면 아무도 실제로 읽지 않기 때문에 그런 일은 일어나지 않습니다!동시에 메인 스레드의 파일에서 인라인으로 읽을 경우 잠재적으로 프로그램의 다른 작업을 차단할 수 있으며 파일 작업이 CPU 바인딩 작업에 비해 매우 느리기 때문에 가시적인 문제를 일으킬 수 있습니다.따라서 내부 작업자 스레드(UV_THREADPOOL_SIZE 환경 변수를 통해 구성 가능)를 사용하여 파일을 운영하는 반면, 이벤트 기반 추상화는 프로그램 관점에서 그대로 작동합니다.

이게 도움이 되길 바랍니다.

libuv 소개

node.js 프로젝트는 브라우저에서 분리된 JavaScript 환경에서 2009년에 시작되었습니다.Google의 V8과 Mark Lhemann의 livev를 사용하여 node.js는 브라우저에 의해 형성된 방식으로 인해 프로그래밍 스타일에 잘 맞는 언어와 이벤트가 발생한 I/O 모델을 결합했습니다.node.js의 인기가 높아짐에 따라 윈도우즈에서 작동하도록 하는 것이 중요했지만 livev는 유닉스에서만 실행되었습니다.kqueue 또는 (e)poll과 같은 커널 이벤트 알림 메커니즘에 해당하는 Windows는 IOCP입니다. libuv는 플랫폼에 따라 libv 또는 IOCP를 중심으로 추상화되었으며 libev 기반 API를 사용자에게 제공합니다.node-v0.9.0 버전의 libublive는 제거되었습니다.

또한 @BusyRich의 Node.js에 있는 이벤트 루프를 설명하는 사진 한 장.


2017년 05월 09일 업데이트

문서 Node.js 이벤트 루프에 따라,

다음 다이어그램은 이벤트 루프의 작업 순서에 대한 간략화된 개요를 보여줍니다.

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

참고: 각 상자는 이벤트 루프의 "위상"이라고 합니다.

단계 개요

  • 타이머: 이 단계는 예약된 콜백을 실행합니다.setTimeout()그리고.setInterval().
  • I/O 콜백: 타이머에 의해 예약된 콜백을 제외한 거의 모든 콜백을 실행합니다.setImmediate().
  • 유휴, 준비: 내부에서만 사용됩니다.
  • poll: 새 I/O 이벤트를 검색합니다. 노드가 적절한 경우 여기서 차단됩니다.
  • 확인:setImmediate()콜백이 여기서 호출됩니다.
  • 콜백 닫기: 예:socket.on('close', ...).

이벤트 루프의 각 실행 사이에 Node.js는 비동기 I/O 또는 타이머를 대기하고 있는지 확인하고 대기 중인 I/O 또는 타이머가 없으면 정상적으로 종료합니다.

NodeJs 아키텍처에는 이벤트 루프가 하나 있습니다.

Node.js 이벤트 루프 모델

노드 응용 프로그램은 단일 스레드 이벤트 기반 모델에서 실행됩니다.그러나 노드는 작업을 수행할 수 있도록 백그라운드에서 스레드 풀을 구현합니다.

Node.js는 이벤트 대기열에 작업을 추가한 다음 이벤트 루프를 실행하는 단일 스레드에서 작업을 픽업합니다.이벤트 루프는 이벤트 대기열의 최상위 항목을 캡처하고 실행한 다음 다음 항목을 캡처합니다.

수명이 길거나 I/O 차단이 있는 코드를 실행할 때 함수를 직접 호출하는 대신 함수가 완료된 후 실행될 콜백과 함께 이벤트 대기열에 함수를 추가합니다.Node.js 이벤트 대기열의 모든 이벤트가 실행되면 Node.js 응용 프로그램이 종료됩니다.

이벤트 루프는 애플리케이션 기능이 I/O에서 차단될 때 문제가 발생하기 시작합니다.

Node.js는 이벤트 콜백을 사용하여 차단 I/O를 기다릴 필요가 없습니다.따라서 차단 I/O를 수행하는 모든 요청은 백그라운드의 다른 스레드에서 수행됩니다.

I/O를 차단하는 이벤트가 이벤트 대기열에서 검색되면 Node.js는 스레드 풀에서 스레드를 검색하고 주 이벤트 루프 스레드 대신 해당 기능을 실행합니다.이렇게 하면 차단 I/O가 이벤트 대기열의 나머지 이벤트를 보류할 수 없습니다.

libuv에서 제공하는 이벤트 루프는 하나뿐이며, V8은 JS 런타임 엔진일 뿐입니다.

자바스크립트 초보자로서, 나는 또한 같은 의심을 품었다, 노드.JS에는 2개의 이벤트 루프가 포함되어 있습니까?V8 기여자 중 한 명과 오랜 연구와 논의 끝에 다음과 같은 개념을 얻었습니다.

  • 이벤트 루프는 JavaScript 프로그래밍 모델의 기본적인 추상 개념입니다.따라서 V8 엔진은 임베더(브라우저, 노드)가 대체하거나 확장할 수 있는 이벤트 루프에 대한 기본 구현을 제공합니다.이벤트 루프의 V8 기본 구현은 여기에서 확인할 수 있습니다.
  • NodeJS에는 노드 런타임에 의해 제공되는 이벤트 루프하나만 있습니다.V8 기본 이벤트 루프 구현이 노드로 대체되었습니다.JS 이벤트 루프 구현

pbkdf2함수에는 JavaScript 구현이 있지만 실제로 수행할 모든 작업을 C++ 측에 위임합니다.

env->SetMethod(target, "pbkdf2", PBKDF2);
  env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
  env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
  env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1);
  NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
  NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
  NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
  NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
  NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
  env->SetMethod(target, "randomBytes", RandomBytes);
  env->SetMethodNoSideEffect(target, "timingSafeEqual", TimingSafeEqual);
  env->SetMethodNoSideEffect(target, "getSSLCiphers", GetSSLCiphers);
  env->SetMethodNoSideEffect(target, "getCiphers", GetCiphers);
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
  env->SetMethodNoSideEffect(target, "getCurves", GetCurves);
  env->SetMethod(target, "publicEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_encrypt_init,
                                         EVP_PKEY_encrypt>);
  env->SetMethod(target, "privateDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_decrypt_init,
                                         EVP_PKEY_decrypt>);
  env->SetMethod(target, "privateEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_sign_init,
                                         EVP_PKEY_sign>);
  env->SetMethod(target, "publicDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_verify_recover_init,
                                         EVP_PKEY_verify_recover>);

리소스: https://github.com/nodejs/node/blob/master/src/node_crypto.cc

Libuv 모듈에는 표준 라이브러리의 일부 특정 기능과 관련된 또 다른 책임이 있습니다.

일부 표준 라이브러리 함수 호출의 경우, Node C++ 측과 Libuv는 이벤트 루프 밖에서 완전히 값비싼 계산을 수행하기로 결정합니다.

대신 스레드 풀이라고 불리는 것을 사용합니다. 스레드 풀은 다음과 같은 계산 비용이 많이 드는 작업을 실행하는 데 사용할 수 있는 네 개의 스레드 시리즈입니다.pbkdf2기능.

기본적으로 Libuv는 이 스레드 풀에 4개의 스레드를 생성합니다.

이벤트 루프에 사용되는 스레드 외에도 애플리케이션 내부에서 발생하는 값비싼 계산을 오프로드하는 데 사용할 수 있는 4개의 스레드가 있습니다.

노드 표준 라이브러리에 포함된 많은 함수들은 자동으로 이 스레드 풀을 사용합니다.pbkdf2함수는 그들 중 하나입니다.

이 스레드 풀의 존재는 매우 중요합니다.

노드는 진정한 단일 스레드가 아닙니다. 계산 비용이 많이 드는 작업을 위해 노드가 사용하는 다른 스레드가 있기 때문입니다.

이벤트 풀이 계산 비용이 많이 드는 작업을 담당하는 경우 Node 애플리케이션은 다른 작업을 수행할 수 없습니다.

우리의 CPU는 스레드 내의 모든 명령을 하나씩 실행합니다.

스레드 풀을 사용하면 계산이 진행되는 동안 이벤트 루프 내에서 다른 작업을 수행할 수 있습니다.

간단히 말해 노드 이벤트 루프는 Javascript 코드가 비동기 코드를 처리하는 데 도움이 되는 아키텍처 수준의 사이클 또는 루프입니다.

이벤트 루프 내부에는 setTimeout, setimmediate, 파일 시스템, 네트워크 요청, 약속 등과 같은 적절한 작업을 처리하는 데 사용되는 루프/사이클이 다릅니다.

언급URL : https://stackoverflow.com/questions/10680601/nodejs-event-loop

반응형