on
노드에 대한 참고 사항
노드에 대한 참고 사항
노드란?
사전 정의부터 시작하겠습니다. Node.js는 V8 엔진에서 실행되며 웹 브라우저 외부에서 자바스크립트 코드를 실행하는 오픈 소스 크로스 플랫폼 백엔드 자바스크립트 런타임 환경입니다. 그렇다면 이것이 정확히 무엇을 의미할까요? 노드 아키텍처의 개요를 제공하는 다이어그램으로 시작하겠습니다.
그래서 여기, 최상위 레벨에는 우리가 실제로 작성한 자바스크립트 프로그램이 있습니다. 이러한 프로그램을 작성할 때 결국 명령줄에서 실행됩니다.
node index.js를 실행할 때, Node 프로젝트를 실행하는데 많은 자바스크립트 프로젝트와 마찬가지로, Node 프로젝트는 실제로 코드를 실행하기 위해 사용하는 종속성에 의해 지원되는데, 그 중 가장 중요한 두 가지는 V8과 libuv입니다.
지방질의
libuv는 파일 시스템 또는 시간 예약 작업 등과 관련된 작업을 수행하기 위해 운영 체제에 대한 노드 액세스를 제공합니다.
V8
V8은 Javaascript 코드를 해석하고 실행하여 브라우저 외부에서 실행할 수 있도록 합니다.
노드 이벤트 루프
우리가 노드 프로그램을 실행할 때마다, 노드는 단일 스레드를 생성하고 그 스레드에 있는 우리의 모든 코드를 실행하며, 그 쓰레드 안에는 이벤트 루프가 있다. 이벤트 루프는 기본적으로 프로그램이 주어진 시간에 수행할 작업을 지시합니다.
이벤트 루프는 어떻게 작동합니까?
명령줄에서 노드 프로그램을 실행하면 파일의 전체 내용이 먼저 실행된 다음 이벤트 루프가 시작됩니다.
이벤트 루프는 실행을 계속하기 전에 몇 가지 조건을 확인하는 동안 루프로 생각할 수 있습니다. 조건이 true를 유지하는 한, 루프는 반복적으로 실행되며, 루프의 각 라이프사이클은 tick 으로 알려져 있습니다.
그러면 이벤트 루프가 다른 체크 표시를 계속할지 여부를 결정하기 위해 어떤 조건을 점검합니까?
먼저 이벤트 루프는 setTimeout 및 setInterval과 같은 보류 중인 타이머 이벤트가 있는지 확인합니다.
그런 다음 지정된 포트에서 서버 수신과 같은 보류 중인 OS 작업이 있는지 확인합니다.
또한 파일에서 읽기와 같은 fs 모듈 작동과 같은 보류 중인 작업이 있는지 확인한다.
노드가 다른 틱을 처리해야 한다고 결정하면 실제로 어떤 일이 발생합니까?
첫 번째 단계는 노드가 대기 중인 타이머를 살펴보고 호출할 수 있는 기능이 있는지 살펴보는 것입니다. 따라서 노드는 이러한 setTimeout 및 setInterval 함수를 살펴보고 해당 함수에 전달된 함수가 실행될 준비가 되었는지 확인합니다.
그런 다음 노드에서는 보류 중인 OS 작업 및 작업을 살펴보고 작업을 실행할 준비가 되면 관련 콜백을 호출합니다.
이 단계 후 노드가 새 이벤트가 발생할 때까지 기다리는 동안 실행이 일시적으로 일시 중지됩니다. 그 후 setImmediate 타이머, 함수 콜백이 실행된다. 마지막으로, 닫기 이벤트 콜백은 소켓.on( 닫기 )과 같이 처리됩니다.
이것이 이벤트 루프의 각 체크 표시를 처리하는 방법입니다.
노드가 단일 나사산입니까?
단일 스레드는 명령이 단일 시퀀스로 실행되는 것을 의미하므로 본질적으로 한 번에 한 가지 일이 발생한다는 의미입니다. 성능, 특히 멀티코어 프로세서의 경우 병목 현상이 발생할 수 있으므로 단일 스레드를 사용해도 이점을 활용할 수 없습니다.
노드 하나가 나사산이고 그게 나쁜 건가요? 음, 노드는 그 자체로 단일 스레드가 아닙니다. 노드의 이벤트 루프는 단일 스레드이지만 일부 노드 프레임워크와 표준 라이브러리는 단일 스레드되지 않습니다.
파일 시스템(fs) 모듈 기능과 같은 일부 기능의 경우 일부 암호화 모듈 기능 등이 있습니다. 노드의 C++ 측면 중 하나인 Libuv는 노드가 여러 스레드를 이용할 수 있도록 스레드 풀을 만든다.
const crypto = require('crypto'); const start = Date.now(); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('1:', Date.now() - start); });
예를 들어, 이 프로그램을 실행할 때 스레드.js라는 이름의 프로그램을 예로 들어보자. 실행을 완료하는데 약 400ms가 소요됩니다.
다음 프로그램을 보면, 이 기능은 5번 반복됩니다. 노드가 완전히 단일 나사산이라고 가정할 경우, 이 작업은 기본적으로 5배가 걸립니다.
const crypto = require('crypto'); const start = Date.now(); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('1:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('2:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('3:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('4:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('5:', Date.now() - start); });
하지만, 실행되었을 때, 우리는 다음과 같은 것을 가지고 있습니다.
음, 흥미로운 일이 여기서 일어납니다. 처음 4개의 기능은 거의 동시에 실행되지만 다섯 번째 기능은 시간이 좀 더 걸립니다. 왜 그럴까요? libuv가 생성하는 스레드 풀에는 기본적으로 4개의 스레드가 있습니다. 그러나 process.env를 사용하여 수정할 수 있습니다.UV_ThREADPOOL_SIZE입니다. 스레드풀 크기를 5개의 스레드로 편집하여 차이가 있는지 확인해 보겠습니다.
이제 저희 프로그램은 이렇습니다.
process.env.UV_THREADPOOL_SIZE = 5; const crypto = require('crypto'); const start = Date.now(); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('1:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('2:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('3:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('4:', Date.now() - start); }); crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log('5:', Date.now() - start); });
실행 시 다음과 같은 결과가 나타납니다.
이제 모든 기능을 실행하는 데 대략 동일한 시간이 소요된다는 것을 알 수 있습니다. 그렇다고 해서 성능을 향상시키기 위해 더 많은 스레드를 무한정 만들 수 있는 것은 아니며, 사용할 수 있는 스레드의 양은 컴퓨터 리소스의 함수이기 때문에 제한적이며, 따라서 새 스레드를 스팸 처리하면 수익이 감소하게 됩니다.
노드가 단일 스레드가 아닌 방법은 스레드풀뿐만이 아닙니다. 예를 들어 네트워킹과 같은 일부 작업의 경우, 노드의 http 모듈을 사용하여 수행할 수 있으며 운영체제에 의해 실제로 처리된다. Libuv는 이 작업을 OS에 위임하므로 코드에 차단이 없습니다.
const https = require('https'); const crypto = require('crypto'); const fs = require('fs'); const start = Date.now(); function doRequest() { https .request('https://www.google.com', (res) => { res.on('data', () => {}); res.on('end', () => { console.log('Network:', Date.now() - start); }); }) .end(); } function doHash(e) { crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => { console.log(`Hash: ${e}`, Date.now() - start); }); }t doRequest(); fs.readFile('multitask.js', 'utf8', () => { console.log('FS: ', Date.now() - start); }); doHash(1); doHash(2); doHash(3); doHash(4);
만약 우리가 multitask.js 위의 이 프로그램을 본다면, 우리는 http 모듈을 사용하는 네트워크 요청, 암호화 모듈을 이용한 해싱 함수, 그리고 파일 시스템 함수를 가지고 있다. 네트워크 요청이 먼저 호출되고, 그 다음에 파일 판독값이 호출되며, 그 다음에 해싱 기능이 호출됩니다. 이게 어떻게 실행될지 아십니까? 잠깐 시간을 내서 알아낼 수 있는지 알아보세요.
이것이 우리가 프로그램을 실행할 때 얻을 수 있는 것입니다.
그런데 잠깐, 네트워크 요청이 OS에 위임되었다고 하셨는데 다른 작업보다 왜 이렇게 오래 걸립니까? 제가 이 기사를 쓸 때, 아마 이건 제 인터넷 연결의 기능일 겁니다. 만약 여러분이 프로그램을 복사해서 직접 실행하려고 한다면, 훨씬 더 나은 결과를 얻을 수 있을 겁니다.
해싱 기능만큼 파일을 읽는 데 시간이 걸리는 이유는 무엇입니까? 하드 드라이브에서 파일을 읽는 속도가 더 빠를까요? 이것은 기본 스레드풀 크기의 함수이며, 4개의 해싱 함수와 readFile 연산이 있습니다. 거의 같은 시간이 걸리는 이유는 readFile 프로세스에는 일부 유휴 지점이 있기 때문입니다. 이러한 지점에서는 스레드가 완전히 유휴 상태가 되지 않도록 해당 스레드에 해싱 기능이 할당됩니다. 이전에 했던 것처럼 스레드풀 크기를 5로 늘리면 됩니다. 이것이 우리의 결과입니다.
파일 시스템 작동이 훨씬 더 빠르게 수행된다는 것을 알 수 있습니다.
노드(Node)에 대해 배우면서 배운 몇 가지 흥미로운 내용입니다. 유용하게 사용하시기 바랍니다.
from http://gong-tech.tistory.com/15 by ccl(A) rewrite - 2021-09-22 05:01:31