이벤트 디멀티플렉싱은 무엇인가?
이 매커니즘은 감시된 일련의 리소스들로부터 들어오는 I/O 이벤트를 수집하여 큐에 넣고 처리할 수 있는 새 이벤트가 있을 때까지 차단함.
socketA,pipeB
wahchedList.add(socketA, FOR_READ); // [1]
while(events = demultiplexer.watch(watchedList)){ //[2]
foreach(event in events){
//read는 블록되지 않으며 비어있을지언정, 항상 데이터를 반환함.
data = event.resource.read();
if(data === RESOURCE_CLOSED) demultiplexer.unwatch(event.resource);
else resolve(data);
}
}
[2]에서 디멀티플렉서에 감시할 것들을 추가. 이 호출은 동기식이며, 감시 대상 중 하나라도 데이터를 리턴하기 전까지 차단됨. 이때 이벤트 디멀티플렉서는 호출로부터 복귀하여 새로운 이벤트들을 처리할 수 있게됨(비동기)
[3]에서 디멀티플렉서가 반환한 이벤트가 처리됨. 여기를 Event Loop라고 부르며, 이곳에 도달했다는 것은 역으로 읽기 작업이 완료되었다는 것이므로 차단되지 않은 상황이라고 보증됨.
구체적으로, 바로 값을 가져올 수 없는 작업형 함수를 만날 경우(I/O) 일단 약속된 상수 값을 리턴시키고 해당 함수를 디멀티플렉서에 추가함. 이때 완료 후 호출될 핸들러(callback)와 이벤트가 디멀티플렉서에 들어감. 이벤트가 완료될 경우 디멀티플렉서가 이벤트를 반환함. 반환된 이벤트는 큐에 push되고, 이벤트 루프는 이벤트 큐를 순회하며 각각의 이벤트들에 대한 핸들러를 실행합니다.
이벤트 디멀티플렉싱 패턴을 사용하면 Busy-waiting을 사용하지 않고도 단일 스레드 내에서 여러 I/O 작업을 처리할 수 있습니다. 기존의 동기식 블로킹 I/O가 스레드를 만들어 다중 I/O 작업을 처리했다면 이는 하나의 스레드만 사용하여 시간에 따라 작업을 분리해 처리합니다. 따라서 위의 그림처럼 유휴시간(파란색)을 최소로 줄일 수 있습니다. 또한 프로세스 간의 경쟁 혹은 여러 스레드들의 동기화 걱정이 없는 훨씬 간단한 동시성 전략을 사용할 수 있습니다.
반응자 패턴 (Reactor Pattern)
리엑터 패턴의 핵심은 각 I/O 작업과 관련된 핸들러(callback)를 갖는 것입니다. 이 핸들러는 이벤트 루프에 의해 처리되는 즉시 호출됩니다. 리엑터 패턴은 위에서 설명한 이벤트 디멀티플렉싱을 활용하는 패턴입니다.
- 애플리케이션이 요청을 이벤트 디멀티플렉서에 제출하여, 새로운 IO 작업을 생성한다. 애플리케이션은 또한 작업이 완료되면 호출할 핸들러(콜백)를 지정한다. 새로운 요청을 이벤트 디멀티플렉서에 제출하는 것은 논블로킹 요청으로, 즉시 애플리케이션에 통제권을 반환한다.
- IO 작업 세트가 완료되면, 이벤트 디멀티플렉서가 해당 이벤트 세트를 이벤트 큐로 푸시 한다.
- 이 때, 이벤트 루프는 이벤트 큐 항목에서 반복된다.
- 각 이벤트에 연결된 핸들러가 호출된다.
- a. 애플리케이션 코드의 일부인 핸들러(콜백) 실행이 완료되면, 이벤트 루프를 다시 제어한다. 5b. 콜백이 실행되는 동안, 새로운 비동기 작업을 요청할 수 있으며, 이는 이벤트 디멀티플렉서에 새로운 항목 추가를 야기한다.
- 이벤트 큐의 모든항목이 처리되면, 이벤트 루프는 이벤트 디멀티플렉서를 다시 차단하며, 이 경우 새로운 이벤트가 가능해질 때 다시 트리거한다.
즉, 리엑터 패턴은 관찰 대상 리소스가 반응(콜백)하면 해당 이벤트의 핸들러를 추적해 실행하는 디자인 패턴입니다. 노드의 전신이라 할 수 있는 패턴입니다.
Node.js는 Reactor 패턴을 사용하며 JS Core API, V8 및 libuv에 의존하는 구조라고 할 수 있습니다.
- Binding: libuv 외 로우레벨 기능들을 JS에 랩핑하고 사용 가능하게 만들어 줌.
- V8: 구글에서 만든 크롬용 JS 엔진. V8 덕에 Node.js 가 매우 빠르고 효율적으로 작동한다.
- JS Core API: Node.js API 를 구현하는 자바스크립트 코어
참고 사이트 : Plus Ultra
'언어 · 런타임 > JavaScript' 카테고리의 다른 글
JavaScript DFS에서 [...path, i]와 push/pop 비교 - 상태 복사 vs 복원 (0) | 2025.05.08 |
---|---|
백준 JavaScript 제출법 총정리: fs, readline 입출력 예제와 주의사항 (0) | 2025.04.24 |
JavaScript란? (0) | 2024.03.05 |
JavaScript 배열 복사: spread와 map의 차이 완벽 정리 (얕은 복사 vs 깊은 복사) (0) | 2024.02.05 |