답변:
node.js & v8의 소스 코드를 개인적으로 읽었습니다.
네이티브 모듈을 작성하기 위해 node.js 아키텍처를 이해하려고 할 때 비슷한 문제가 발생했습니다.
내가 여기에 게시하는 것은 node.js에 대한 이해이며 이것은 약간 벗어난 것일 수 있습니다.
Libev 는 node.js에서 내부적으로 실행되어 간단한 이벤트 루프 작업을 수행하는 이벤트 루프입니다. 원래 * nix 시스템 용으로 작성되었습니다. Libev는 프로세스가 실행되도록 간단하면서도 최적화 된 이벤트 루프를 제공합니다. libev에 대한 자세한 내용은 여기를 참조 하십시오 .
LibEio 는 입력 출력을 비동기 적으로 수행하는 라이브러리입니다. 파일 디스크립터, 데이터 핸들러, 소켓 등을 처리합니다 . 여기 에서 자세한 내용을 볼 수 있습니다 .
LibUv 는 libeio, libev, c-ares (DNS 용) 및 iocp (Windows asynchronous-io 용) 위에있는 추상화 계층입니다. LibUv는 이벤트 풀에서 모든 io 및 이벤트를 수행, 유지 관리 및 관리합니다. (libeio threadpool의 경우). libUv에 대한 Ryan Dahl의 자습서 를 확인해야합니다 . libUv가 어떻게 작동하는지에 대해 더 이해하기 시작한 다음 libuv 및 v8에서 node.js가 어떻게 작동하는지 이해할 것입니다.
자바 스크립트 이벤트 루프 만 이해하려면 다음 동영상 시청을 고려해야합니다.
비동기 모듈을 작성하기 위해 libeio를 node.js와 함께 사용하는 방법을 보려면 이 예제를 참조하십시오 .
기본적으로 node.js 내에서 발생하는 일은 v8 루프가 실행되고 모든 Javascript 부분뿐만 아니라 C ++ 모듈 (주 문서에서 node.js 자체는 단일 스레드)에서 실행되는 경우입니다. 메인 스레드 외부에있을 때 libev 및 libeio는 스레드 풀에서이를 처리하고 libev는 메인 루프와의 상호 작용을 제공합니다. 내 이해에서 node.js에는 1 개의 영구 이벤트 루프가 있습니다 .v8 이벤트 루프입니다. C ++ 비동기 작업을 처리하기 위해 [libeio & libev를 통해] 스레드 풀을 사용하고 있습니다.
예를 들면 다음과 같습니다.
eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);
모든 모듈에 나타나는 것은 일반적으로 Task
스레드 풀 에서 함수 를 호출하는 것 입니다. 완료되면 AfterTask
메인 스레드에서 함수를 호출합니다 . 반면 Eio_REQUEST
요청 핸들러는 스레드 풀과 메인 스레드 사이의 통신을 제공하는 동기를 갖는 구조 / 객체 일 수 있습니다.
process.nextTick
이벤트 루프 주위의 다음 루프 에서이 콜백을 호출하십시오. 이것은 setTimeout (fn, 0)의 단순한 별칭이 아니며 훨씬 효율적입니다. 이것은 어떤 이벤트 루프를 의미합니까? V8 이벤트 루프?
논의 된 실체 (예 : libev 등)가 오랜 시간이라는 사실 때문에 관련성을 잃어 버린 것처럼 보이지만 질문은 여전히 큰 잠재력을 가지고 있다고 생각합니다.
오늘, 추상 유닉스 환경, 노드의 맥락에서 추상 예제를 사용하여 이벤트 중심 모델의 작동을 설명하려고합니다.
프로그램의 관점 :
위의 이벤트 기계를 libuv AKA 이벤트 루프 프레임 워크라고합니다. 노드는이 라이브러리를 활용하여 이벤트 중심 프로그래밍 모델을 구현합니다.
노드의 관점 :
대부분의 기능은 이러한 방식으로 제공되지만 파일 작업의 일부 (비동기 버전)는 추가 스레드를 사용하여 수행되며 libuv에 잘 통합됩니다. 네트워크 I / O 조작은 데이터 등으로 응답하는 다른 엔드 포인트와 같은 외부 이벤트를 예상하여 대기 할 수 있지만 파일 조작은 노드 자체에서 일부 작업이 필요합니다. 예를 들어, 파일을 열고 fd에 데이터가 준비 될 때까지 기다리면 아무도 실제로 읽지 않으므로 파일이 발생하지 않습니다! 동시에, 메인 스레드의 파일 인라인에서 읽을 경우, 프로그램의 다른 활동을 잠재적으로 차단할 수 있으며 파일 조작이 CPU 바운드 활동에 비해 매우 느리기 때문에 가시적 인 문제점을 야기 할 수 있습니다. 따라서 내부 작업자 스레드 (UV_THREADPOOL_SIZE 환경 변수를 통해 구성 가능)가 파일에서 작동하는 데 사용됩니다.
도움이 되었기를 바랍니다.
Node.js를 자바 스크립트 환경이 브라우저에서 분리로 프로젝트는 2009 년에 시작되었다. 구글의 사용 V8 과 마크 레만의 libev I의 모델을 결합 Node.js를, / O를 - evented - 잘 프로그래밍의 스타일에 적합 한 언어; 브라우저에 의해 형성된 방식 때문입니다. node.js의 인기가 높아짐에 따라 Windows에서 작동하도록하는 것이 중요했지만 libev는 Unix에서만 실행되었습니다. kqueue 또는 (e) poll과 같은 커널 이벤트 알림 메커니즘에 해당하는 Windows는 IOCP입니다. libuv는 플랫폼에 따라 libev 또는 IOCP를 추상화하여 사용자에게 libev 기반 API를 제공했습니다. libuv libev의 node-v0.9.0 버전 에서 제거되었습니다 .
@ BusyRich의 Node.js의 이벤트 루프를 설명하는 하나의 그림
2017 년 5 월 9 일 업데이트
이 문서 당 Node.js를 이벤트 루프 ,
다음 다이어그램은 이벤트 루프의 작업 순서에 대한 간략한 개요를 보여줍니다.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
참고 : 각 상자는 이벤트 루프의 "단계"라고합니다.
단계 개요
setTimeout()
및에 의해 예약 된 콜백을 실행 setInterval()
합니다.setImmediate()
합니다.setImmediate()
콜백이 여기에서 호출됩니다.socket.on('close', ...)
.이벤트 루프의 각 실행 사이에서 Node.js는 비동기 I / O 또는 타이머를 기다리고 있는지 확인하고없는 경우 깨끗하게 종료됩니다.
In the node-v0.9.0 version of libuv libev was removed
" 을 인용 했지만 nodejs에 이에 대한 설명이 없습니다 changelog
. github.com/nodejs/node/blob/master/CHANGELOG.md . 그리고 libev가 제거되면 nodejs에서 비동기 I / O가 어떻게 수행됩니까?
NodeJs 아키텍처에는 하나의 이벤트 루프가 있습니다.
노드 애플리케이션은 단일 스레드 이벤트 중심 모델에서 실행됩니다. 그러나 노드는 작업을 수행 할 수 있도록 백그라운드에서 스레드 풀을 구현합니다.
Node.js는 이벤트 큐에 작업을 추가 한 다음 이벤트 루프를 실행하는 단일 스레드가이를 선택합니다. 이벤트 루프는 이벤트 큐의 최상위 항목을 잡고 실행 한 후 다음 항목을 가져옵니다.
수명이 길거나 I / O를 차단하는 코드를 실행하면 함수를 직접 호출하는 대신 함수가 완료된 후 실행될 콜백과 함께 함수를 이벤트 큐에 추가합니다. Node.js 이벤트 큐의 모든 이벤트가 실행되면 Node.js 애플리케이션이 종료됩니다.
응용 프로그램 기능이 I / O에서 차단되면 이벤트 루프가 문제를 일으키기 시작합니다.
Node.js는 이벤트 콜백을 사용하여 I / O 차단을 기다릴 필요가 없습니다. 따라서 차단 I / O를 수행하는 모든 요청은 백그라운드의 다른 스레드에서 수행됩니다.
I / O를 차단하는 이벤트가 이벤트 큐에서 검색되면 Node.js는 스레드 풀에서 스레드를 검색하고 기본 이벤트 루프 스레드 대신 해당 함수를 실행합니다. 이는 차단 I / O가 이벤트 큐의 나머지 이벤트를 보류하지 못하게합니다.
자바 스크립트 초보자로서 NodeJS에 2 개의 이벤트 루프가 포함되어 있습니까? V8 기고자 중 한 사람과 오랜 연구와 토론 끝에 다음과 같은 개념을 얻었습니다.
이 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
함수 와 같이 계산 비용이 많이 드는 작업을 실행하는 데 사용할 수있는 일련의 4 개의 스레드입니다 .
기본적으로 Libuv는이 스레드 풀에 4 개의 스레드를 만듭니다.
이벤트 루프에 사용되는 스레드 외에도 애플리케이션 내부에서 발생하는 고가의 계산을 오프로드하는 데 사용할 수있는 다른 4 개의 스레드가 있습니다.
Node 표준 라이브러리에 포함 된 많은 기능이이 스레드 풀을 자동으로 사용합니다. 그만큼pbkdf2
기능은 그들 중 하나를 제공합니다.
이 스레드 풀의 존재는 매우 중요합니다.
따라서 노드는 계산적으로 비싼 작업을 수행하는 데 사용하는 다른 스레드가 있기 때문에 실제로 단일 스레드가 아닙니다.
이벤트 풀이 계산 비용이 많이 드는 작업을 담당하는 경우 노드 응용 프로그램은 다른 작업을 수행 할 수 없습니다.
우리 CPU는 스레드 내부의 모든 명령을 하나씩 실행합니다.
스레드 풀을 사용하면 계산이 진행되는 동안 이벤트 루프 내에서 다른 작업을 수행 할 수 있습니다.