Node.js Reactor 패턴

mosad2334·2022년 1월 17일
0

Reactor 패턴

Node.js 비동기 특성의 핵심: Reactor 패턴

  • 단일 스레드 아키텍처
  • 논 블로킹 I/O

I/O는 속도가 느림

  • I/O는 컴퓨터의 기본적인 동작 중 가장 느림

블로킹 I/O

  • 전통적인 블로킹 I/O 프로그래밍에서는 요청에 해당하는 함수 호출은 작업이 완료될 떄까지 스레드의 실행이 차단됨
  • 동일한 스레드에서 여러 연결을 처리할 수 없음
  • 동시성을 처리하기 위해서는 새로운 스레드나 프로세스를 시작하거나 풀에서 가져온 스레드를 재사용 해야함
  • 스레드가 I/O 작업으로 차단되어도 분리된 스레드에서 처리되므로 다른 요청의 가용성에는 영향을 미치지 않음

논 블로킹 I/O

  • 시스템 호출은 데이터가 읽히거나 쓰여질 때까지 기다리지 않고 항상 즉시 반환됨
  • 가장 기본적인 패턴:
    • 실제 데이터가 반환될 때까지 루프 내에서 리소스를 적극적으로 폴링(poll)함. 이것을 busy-waiting이라고 함.
    • 폴링 알고리즘은 대부분 엄청난 양의 CPU 시간 낭비를 초래

이벤트 디멀티플렉싱

  • busy-waiting을 대체하는 현재 최신 운영체제의 논 블로킹 리소스 처리를 위한 기본적인 메커니즘
  • 동기 이벤트 디멀티플렉서 또는 이벤트 통지 인터페이스라고 함
  • 감시된 인련의 리소스들로부터 들어오는 I/O 이벤트를 수집하여 큐에 넣고 처리할 수 있는 새 이벤트가 있을 때까지 차단함
  • 바쁜 대기(busy-waiting)기술을 사용하지 않고도 단일 스레드 내에서 여러 I/O 작업을 처리할 수 있음

알고리즘 주요 과정

  1. 리소스를 데이터 구조(List)에 추가한다.
  2. 이벤트 통지자에 감시할 리소스 그룹을 설정한다.
  • 이 호출은 동기식으로, 감시 대상 자원 중 하나라도 읽을 준비가 될 때까지 차단됨
  • 이벤트 디멀티플렉서는 호출로부터 복귀하여 새로운 일련의 이벤트들을 처리할 수 있게됨
  1. 이벤트 디멀티플렉서에 의해 반환된 각 이벤트가 처리됨.
  • 이 시점에서, 각 이벤트와 관련된 리소스는 읽기 작업을 위한 준비가 되어 있고 차단되지 않는 상황이라는 것이 보증됨
  • 모든 이벤트가 처리되고 나면 다시 디멀티플렉서에서 처리 가능한 이벤트가 발생할 때까지 차단됨.
  • 이를 이벤트 루프라고 함.

Reactor 패턴 소개

  • 이벤트 디멀티플렉싱에 특수화된 Reactor 패턴
  • 각 I/O 작업과 관련된, 이벤트가 생성되어 이벤트 루프에 의해 처리되는 즉시 호출되는 핸들러가 핵심개념
  • 어플리케이션은 특정 시점에서 블로킹 없이 리소스에 액세스하려는 요청을 표시
  • 해당 처리가 완료되는 다른 시점에서 호출될 핸들러를 제공
  • Node.js 어플리케이션은 이벤트 디멀티플렉서에 더 이상 보류 중인 작업이 없고, 이벤트 큐에서 더이상 처리할 이벤트가 없으면 자동으로 종료됨.

Node.js 핵심 패턴: 일련의 관찰 대상 리소스에서 새 이벤트를 사용할 수 있을 떄까지 차단하여 I/O를 처리한 다음, 각 이벤트를 관련 해들러로 전달함으로써 반응함.

Reactor 패턴을 사용하는 어플리케이션에서 어떤 일이 발생하는지는
👉 What is Reactor Pattern in Node.js ?

Node.js의 논 블로킹 엔진 libuv

  • 각 운영체제에는 이벤트 디멀티플렉서에 대한 자체 인터페이스가 있음
    • Linux: epoll
    • Mac OS X: kqueue
    • Windows: I/O Completion Port(IOCP)
  • 각 I/O 작업은 동일한 OS 내에서도 리소스 유형에 따라 매우 다르게 작동할 수 있는데, 이러한 불일치 때문에 Node.js 코어 팀이 libuv 라는 C 라이브러리를 만듬.
    • 이를 통해 모든 주요 플랫폼과 호환됨
    • 서로 다른 유형의 리소스들의 논 블로킹 동작을 표준화 할 수 있음
  • 오늘날 libuv는 Node.js의 하위 수준의 I/O 엔진을 나타냄
  • 기본 시스템 호출 추상화 외에 이벤트 루프를 만들고, 이벤트 큐를 관리하고, 비동기 입출력 작업을 실행하고, 다른 유형의 작업을 큐에 담기 위한 API들을 제공

libuv에 더 많은 것에 대한 정보는
👉 An Introduction to libuv

Node.js를 위한 구조

  • 리액터 패턴과 libuv가 Node.js의 기본 구성 요소이지만 전체 플랫폼을 구축하려면 다음 세가지 구성 요소가 필요함
    • libuv와 기타 낮은 수준의 기능들을 JavaScript에 랩핑하고 사용 가능하도록 해주는 바인딩 세트.
    • V8, Google에서 Chrome 브라우저 용으로 개발한 JavaScript 엔진
    • 상위 수준의 Node.js API를 구현하고 있는 코어 JavaScript 라이브러리(노드 코어라고 함)

위 내용은 Node.js 디자인 패턴(Mario Casciaro, Luciano Mammino 저)의 챕터 1의 3단락 Reactor 패턴에 관한 내용을 요약한 것이다.
리액터 부분이 글 만으로는 이해가 안되서 이것저것 아래에 달아 놓은 링크를 참조하고 자료 끌어모아 읽고 영상들을 시청한 뒤 글을 요약하면서 머리를 정리했다.
참고로 아래의 모든 링크가 관련 내용은 아니고, 이번에 찾은 자료인데 나중에도 참고하고 싶어서 메모용으로 달아 놓은 것도 있다...

아직 헷갈리는 것(대강 해결)

포스팅 뒤 6시간(잤다) 지나고 로우 레벨로 살펴보는 Node.js 이벤트 루프를 느슨하게 읽어봄.

대표적인 잘못된 개념들

이벤트 루프는 자바스크립트 엔진 내부에 있다
대표적인 잘못된 개념들 중 하나는 바로 이벤트 루프가 자바스크립트 엔진(V8, Spider Monkey 등)의 일부라는 것이다. 하지만 이벤트 루프는 단지 자바스크립트 코드를 실행하기위해 자바스크립트 엔진을 이용하기만 할 뿐이다. (역주: 실제로 V8 엔진에는 이벤트 루프를 관리하는 코드가 없다. Node.js나 브라우저가 이벤트 루프를 담당하는 것)

...


몇 가지 일반적인 질문들

그럼 자바스크립트는 정확히 어디에서 실행되는건가요?
이 포스트를 처음 읽을 때는 자바스크립트가 정확히 어디서 실행되는지 헷갈릴 수 있다.

앞서 말했듯이 이벤트 루프 자체에서 V8 또는 다른 엔진을 사용하여 자바스크립트를 실행하는 것이고 이때, 단 하나의 스레드를 사용하여 자바스크립트가 실행되는 것이다. 실행 자체는 동기적이고, 현재 실행시킨 자바스크립트의 실행이 완료되지 않는다면 이벤트 루프 또한 진행되지 않는다.

엥. 왠지 당연하게 V8에서 이벤트 루프를 관리한다고 착각함. 그래서 node.js의 libuv와 V8역활을 엄청 헷갈렸구나.
그리고 어쨌든 이벤트 루프는 무엇입니까?는 '브라우저'의 이벤트 루프를 설명하는 것을 확인함.(제대로 안봤네..)
그래서 node.js에서는 libuv에서 이벤트 루프를 관리하고 프론트엔드에서는 브라우저가 이벤트 루프를 담당하는 것으로..

그리고 JavaScript Event Loop vs Node JS Event Loop에서 드디어 어떤 차이가 있는 지 떠먹여 주는 것을 습득.

결론은 둘 다 JavaScript의 싱글스레드에서 동시에 전달되는 서비스 요청을 해결하기 위해 이벤트 루프를 이용한 패턴을 이용한 별개의 처리 모델이라고 이해했다.

참조

관련 있는 참조

별 관련 없는 참조(메모용)

profile
Web Developer / Read "KEEP CALM CARRY ON" Poster

0개의 댓글