"비동기식"이라는 용어의 대규모 인기는 Web 2.0의 물결에 있었고, 이는 JavaScript와 Ajax로 웹을 휩쓸 었습니다. 그러나 비동기식은 대부분의 높은 수준의 프로그래밍 언어에서는 드 rare니다. PHP는이 기능을 가장 잘 반영합니다. 비동기 적으로 차단할뿐만 아니라 여러 스레드도 제공하지 않습니다. PHP는 동기식 차단 방식으로 실행됩니다. 이러한 장점은 프로그래머가 비즈니스 로직을 순서대로 작성하는 데 도움이되지만 복잡한 네트워크 응용 프로그램에서는 차단하면 더 동시에 실패합니다.
서버 측에서 I/O는 매우 비싸고 분산 I/O는 더 비쌉니다. 백엔드가 자원에 신속하게 대응할 수있는 경우에만 프론트 엔드 경험이 향상 될 수 있습니다. Node.js는 기본 프로그래밍 방법 및 설계 개념으로 비동기식을 사용하는 최초의 플랫폼입니다. 비동기 I/O, 이벤트 중심 및 단일 스레딩과 함께 노드의 톤을 형성합니다. 이 기사는 노드가 비동기 I/O를 구현하는 방법을 소개합니다.
1. 기본 개념
"Async"및 "Non-Blocking"은 동일한 소리와 실제 결과 측면에서 평행의 목적을 달성합니다. 그러나 컴퓨터 커널 I/O의 관점에서 볼 때, 차단 및 비 블로킹의 두 가지 방법 만 있습니다. 따라서 비동기/동기화 및 차단/비 블로킹은 실제로 두 가지 다른 것입니다.
1.1 차단 I/O 및 비 블로킹 I/O
I/O 차단의 한 가지 특징 중 하나는 호출 후 통화가 완료되기 전에 시스템 커널 레벨에서 모든 작업이 완료 될 때까지 기다려야한다는 것입니다. 디스크에서 파일을 읽는 것은 예제로서 시스템 커널이 디스크 검색을 완료하고 데이터를 읽고 데이터를 메모리에 복사 한 후에 종료됩니다.
I/O 차단으로 인해 CPU는 I/O를 기다리고 대기 시간을 낭비하며 CPU의 처리 능력을 완전히 활용할 수 없습니다. 비 블로킹 I/O의 특성은 통화 직후에 반환되며 CPU 타임 슬라이스를 사용하여 수익 후 다른 트랜잭션을 처리 할 수 있다는 것입니다. 전체 I/O가 완료되지 않으므로 즉시 반환되는 것은 비즈니스 계층이 기대하는 데이터가 아니라 현재 통화 상태입니다. 완전한 데이터를 얻으려면 응용 프로그램은 I/O 작업이 완료되었는지 여부를 확인하기 위해 I/O 작업을 반복적으로 호출해야합니다 (즉, 폴링). 폴링 기술에는 다음이 필요합니다.
1. 읽기 : 반복 통화로 I/O 상태를 확인하는 것이 가장 독창적이고 가장 낮은 성능 방법입니다.
2. 선택 : 읽기 개선, 파일 디스크립터의 이벤트 상태를 판단하십시오. 단점은 최대 파일 설명자 수가 제한되어 있다는 것입니다.
3.Poll : 최대 번호 제한을 피하기 위해 링크 된 목록을 사용하여 선택하는 개선 사항이지만 설명자가 많으면 성능이 여전히 매우 낮습니다.
4.epoll : 폴링 중에 I/O 이벤트가 확인되지 않으면 이벤트가 발생할 때까지 잠을 자고 깨어납니다. 이것은 Linux에서 가장 효율적인 I/O 이벤트 알림 메커니즘입니다.
폴링은 전체 데이터 수집을 보장하기 위해 비 블로킹 I/O의 필요성을 충족하지만 응용 프로그램의 경우 I/O가 완전히 반환 될 때까지 기다려야하기 때문에 여전히 일종의 동기화로 계산 될 수 있습니다. 대기 중 CPU는 파일 디스크립터의 상태를 가로 지르거나 이벤트가 발생하기를 기다리는 데 동면하는 데 사용됩니다.
1.2 이상과 현실에서 비동기 I/O
완벽한 비동기 I/O는 비 블로킹 호출을 시작하는 응용 프로그램이어야하며 폴링없이 다음 작업을 직접 처리 할 수 있으며 I/O가 완료된 후 신호 또는 콜백을 통해 데이터를 응용 프로그램으로 전달할 수 있습니다.
실제로 비동기 I/O는 다른 운영 체제에서 다른 구현을 가지고 있습니다. 예를 들어, *Nix 플랫폼은 사용자 정의 스레드 풀을 채택하고 Windows 플랫폼은 IOCP 모델을 채택합니다. 노드는 플랫폼 호환성 판단을 캡슐화하기위한 추상 캡슐화 계층으로서 libuv를 제공하고, 상단 노드와 하부 플랫폼의 비동기 I/O의 구현이 독립적인지 확인합니다. 노드는 단일 스레드라고 자주 언급한다는 점을 강조해야합니다. 이는 JavaScript 실행이 단일 스레드에 있고 실제로 노드 내에서 I/O 작업을 완료하는 다른 스레드 풀도 있습니다.
2. 노드의 비동기 I/O
2.1 이벤트 루프
노드의 실행 모델은 실제로 이벤트 루프입니다. 프로세스가 시작되면 노드는 무한 루프를 생성하고 루프 본체를 실행하는 각 프로세스가 진드기가됩니다. 각 진드기 과정은 처리를 기다리는 이벤트가 있는지 확인하는 것입니다. 그렇다면 이벤트 및 관련 콜백 기능이 제거됩니다. 관련 콜백 함수가 있으면 실행되고 다음 루프가 입력됩니다. 더 이상 이벤트 처리가 없으면 프로세스를 종료하십시오.
2.2 관찰자
각 이벤트 루프에는 여러 관찰자가 있으며 이러한 관찰자에게 요청하여 처리 할 이벤트가 있는지 확인할 수 있습니다. 이벤트 루프는 일반적인 생산자/소비자 모델입니다. 노드에서는 이벤트에서 주로 네트워크 요청, 파일 I/O 등에서 이벤트가 발생합니다. 이러한 이벤트에는 해당 네트워크 I/O 관찰자, 파일 I/O 관찰자 등이 있습니다. 이벤트 루프는 관찰자로부터 이벤트를 가져와 처리합니다.
2.3 요청 객체
JavaScript에서 I/O 작업을 수행하는 커널로 전환하는 동안 요청 객체라는 중간 제품이 있습니다. JS 호출에서 내장 모듈에 이르기까지 Windows에서 가장 간단한 fs.open () (파일을 열고 지정된 경로 및 매개 변수에 따라 파일 디스크립터를 얻음)에서 Libuv를 통해 시스템 호출을 실제로 UV_FS_OPEN () 메소드라고합니다. 호출 프로세스 중에 FSREQWRAP 요청 객체가 생성되고 JS 계층에서 전달 된 매개 변수 및 메소드 가이 요청 객체에서 캡슐화됩니다. 우리가 가장 염려하는 콜백 함수는이 객체의 oncompete_sym 속성에 설정됩니다. 객체가 래핑 된 후 FSREQWRAP 객체를 스레드 풀에 밀어 넣고 실행을 기다립니다.
이 시점에서 JS 호출은 즉시 반환되며 JS 스레드는 계속해서 후속 작업을 수행 할 수 있습니다. 현재 I/O 작업은 스레드 풀에서 실행을 기다리고 있으며 비동기 호출의 첫 번째 단계를 완료합니다.
2.4 콜백을 실행합니다
콜백 알림은 비동기 I/O의 두 번째 단계입니다. 스레드 풀에서의 I/O 작업이 호출되면, 얻은 결과가 저장된 다음 IOCP에 현재 객체 작업이 완료되고 스레드가 스레드 풀을 반환한다는 사실을 알립니다. 각 진드기 실행 중에 이벤트 루프의 I/O 관찰자는 관련 메소드를 호출하여 스레드 풀에 완료된 요청이 있는지 확인합니다. 존재하는 경우 요청 객체가 I/O 관찰자의 대기열에 추가 된 다음 이벤트로 처리됩니다.
3. 비 I/O 비동기 API
타이머 settimeout (), setinterval (), process.nexttick () 및 setimmdiate ()와 같이 노드의 I/O와 관련이없는 비동기 API도 여기에 간략하게 소개됩니다.
3.1 타이머 API
settimeout () 및 setinterval ()의 브라우저 측의 API는 일관성이 있습니다. 그들의 구현 원칙은 비동기 I/O와 유사하지만 I/O 스레드 풀의 참여는 필요하지 않습니다. 타이머 API를 호출하여 생성 된 타이머는 타이머 관찰자 내부의 빨간색과 검은 색 트리에 삽입됩니다. 각 이벤트 루프의 진드기는 빨간색과 검은 색 트리의 타이머 객체를 반복하여 시간 시간이 초과되었는지 확인합니다. 초과하면 이벤트가 형성되고 콜백 함수가 즉시 실행됩니다. 타이머의 주요 문제는 타이밍 시간이 특히 정확하지 않다는 것입니다 (공차 내에서 밀리 초).
3.2 비동기 작업 실행 API
노드가 나타나기 전에 많은 사람들 이이 문제를 즉시 비동기 적으로 수행하기 위해이를 호출 할 수 있습니다.
코드 사본은 다음과 같습니다.
settimeout (function () {
// TODO
}, 0);
이벤트 루프의 특성으로 인해 타이머가 충분히 정확하지 않으며 빨간색과 검은 색 트리를 사용하려면 타이머를 사용해야하며 다양한 작동 시간의 복잡성은 O (log (n))입니다. process.nexttick () 메소드는 콜백 함수를 큐에 넣고 다음 진드기에서 꺼내어 실행합니다. 복잡성은 O (1)이며 더 효율적입니다.
또한, 상기 방법과 유사한 setimmediate () 메소드가 있으며, 콜백 함수의 실행을 지연시킵니다. 그러나 이벤트 루프는 관찰자를 순서대로 점검하기 때문에 전자는 후자보다 우선 순위가 높습니다. 또한, 이전 콜백 함수는 배열에 저장되며 각 진드기 라운드는 배열에서 모든 콜백 함수를 실행합니다. 후자의 결과는 링크 된 목록에 저장되며 각 진드기 라운드는 하나의 콜백 함수 만 실행합니다.
4. 이벤트 중심 및 고성능 서버
이전 예제는 노드가 비동기 I/O를 구현하는 방법을 보여줍니다. 실제로 Node는 네트워크 소켓 처리에 비동기 I/O를 적용하며 Node가 웹 서버를 구축하는 기초이기도합니다. 클래식 서버 모델은 다음과 같습니다.
1. 동기 : 한 번에 하나의 요청 만 처리 할 수 있으며 나머지 요청은 대기 상태에 있습니다.
2. 프로세스 당/요청 당 : 각 요청에 대해 하나의 프로세스를 시작하지만 시스템 리소스는 제한되어 있으며 확장 성이 없습니다.
3. 요청 당/요청 당 : 각 요청에 대해 하나의 스레드를 시작합니다. 스레드는 프로세스보다 가볍지 만 각 스레드는 일정량의 메모리를 차지합니다. 큰 동시 요청이 도착하면 메모리가 곧 소진됩니다.
유명한 Apache는 스레드 당/여론 당 형태를 채택하므로 동시성이 높은 곳에 대처하기가 어렵습니다. 노드는 이벤트 중심 메소드를 통해 요청을 처리하여 스레드 생성 및 파괴의 오버 헤드를 저장할 수 있습니다. 동시에 작업을 예약 할 때 운영 체제에 스레드가 적고 컨텍스트 전환 비용도 매우 낮습니다. 노드는 많은 연결이 있더라도 순서대로 요청을 처리 할 수 있습니다.
잘 알려진 서버 NGINX는 또한 멀티 스레딩 방법을 포기하고 노드와 동일한 이벤트 중심 메소드를 채택합니다. 이제 Nginx는 Apache를 교체하는 큰 방법입니다. NGINX는 순수한 C로 작성되었으며 고성능이지만 웹 서버에만 적합하며 역 프라이즈 또는로드 밸런싱 등에 사용되는 데 사용됩니다. 노드는 NGINX와 동일한 기능을 구축 할 수 있으며 다양한 비즈니스를 처리 할 수 있으며 자체 성능도 좋습니다. 실제 프로젝트에서는이를 결합하여 애플리케이션의 최상의 성능을 달성 할 수 있습니다.