본문 바로가기
소프트웨어/디자인 패턴

[1] Reactor Pattern

by Riverandeye 2020. 10. 19.

리액터 패턴이란 동시적으로 발생하는 I/O 혹은 이벤트들을 서비스 핸들러를 통해 Demultiplexing하고

그 결과들을 각각의 할당된 Request Handler 를 통해 수행하는 패턴입니다. 

 

우선 이 패턴에 대해서 알아보기 전에, 필요한 사전 개념들과 시나리오에 대해 이야기 해봅시다. 

 

Blocking I/O

일반적인 Blocking I/O 에서는 해당 작업이 수행될때까지 프로세스나 쓰레드가 작업을 멈추고 기다립니다.

그러다보니, Blocking I/O로 구현된 웹 서버는 여러 요청을 하나의 쓰레드에서 수행할 수 없게 됩니다.

대표적인 예로 버전 2.x Django가 있습니다. (3.0부터는 코루틴 방식으로 비동기 프로그래밍이 가능하다고 합니다)

 

Blocking I/O를 사용한 채로 여러 요청들을 동시에 수행하려면

개별적인 쓰레드 혹은 프로세스를 구성하여 작업을 수행해야 합니다. 

 

Blocking 이기 때문에, 개별 요청에 대한 쓰레드를 구성하여 Response함

쓰레드 풀을 구성해서 사용하거나 하면 쓰레드를 생성하고 제거하는 데의 비용을 줄일 수 있지만,

사실 쓰레드를 구성하고 이용하는 것 만으로도 Context Switching 비용과 메모리가 들게 됩니다. 

 

Non-Blocking I/O

Non-Blocking I/O 모드에서는 Blocking 과는 다르게 I/O 이벤트 발생시

데이터를 읽거나 작성 될 때 까지 기다리지 않고 바로 리턴하게 됩니다.

 

대신 해당 call에 대한 리소스를 사용하기 위해서는 완료될 때 까지 기다려야 하며, 

리소스가 존재하지 않을 땐 사전에 정의된 상수를 리턴하는 식으로 구성됩니다.

Unix에선 fcntl() 함수가 non-blocking 모드를 지원합니다. 

 

그럼 non-blocking에서 리소스를 가져오기 위해서는 어떻게 해야할까요?

기본적으로 busy-waiting 을 수행하는데, 실제 데이터가 리턴 될 때 까지 while문 돌린다고 생각하면 됩니다. 

물론 이렇게 하면 동일한 쓰레드에서 여러 요청을 수행할 수는 있지만 효율적이지는 못합니다. 

기다릴 떄 까지 While문 돌리는것도 결국엔 그 시간동안 CPU 자원을 사용하는 것이기 때문입니다. 

 

Event Demultiplexing

Busy-Waiting이 능사는 아닌 것 같고, 다른 방식을 찾아봐야 합니다. 

동기적으로 이벤트의 상태를 모니터링 해주는 Synchronous Event Demultiplexer는

모니터링할 이벤트들을 목록에 두고, 해당 이벤트들이 완료가 된 경우까지 Block 합니다.

이벤트가 완료가 되어 read 할 수 있는 상태가 되면, 이를 핸들링 해줍니다. 

 

이렇게 구성하면 여러 I/O 작업을 하나의 쓰레드에서 Busy-waiting 없이 수행할 수 있게 됩니다. 

 

이런 식으로 하나의 쓰레드를 "알차게" 쓸 수 있다. 

이 방식의 장점은 이 뿐만 아니라, 멀티 쓰레드를 구성하지 않아도 되는 점에서도 큰 장점이 있습니다. 

in-process race condition, synchronization 에 대해서 고민하지 않아도 되기 때문이죠. 

 

Reactor Pattern

위에서 등장한 알고리즘에다가 각 이벤트 별 핸들러를 정의하면 그게 Reactor Pattern 입니다. 

전체 구조는 다음과 같습니다. 

 

Reactor Pattern

어플리케이션에서 I/O 이벤트가 발생하면, 해당 리퀘스트를 Event Demultiplexer 에게 전달합니다.

여기에, "이벤트가 완전히 종료될 때 수행될" handler 함수를 이벤트에 등록하여 전달합니다. 

I/O가 완료되면 Demultiplexer는 완료된 이벤트를 Queue에 전달하고

Event Loop는 Event Queue의 대상들을 하나 하나 Execution Context 로 옮겨

해당 이벤트에 등록되어 있는 이벤트 핸들러를 수행시킵니다. 

이벤트 핸들러의 수행이 완료되면 어플리케이션의 컨트롤을 Event Loop에게 다시 넘겨 Event Queue에 있는 이벤트들을 가져옵니다. 

Event Queue가 비게 되면, Event Loop는 Event Demultiplexer 에서 다시 Block 되고 이벤트가 트리거되길 기다립니다. 

 

한줄로 정리하면 리액터 패턴은

"관찰하고 있는 리소스" 들로부터 발생하는 이벤트들이 발생할 때 까지 "Block" 하고

이벤트 발생 시 해당 이벤트와 연결되어있는 handler에게 dispatch(전달) 함으로써 "반응(React)"하는 패턴입니다.

 

Reference

Node.js Design Patterns

'소프트웨어 > 디자인 패턴' 카테고리의 다른 글

[6] 싱글턴 패턴  (0) 2020.11.05
[5] 팩토리 메소드 패턴  (0) 2020.10.29
[4] Decorator Pattern  (0) 2020.10.28
[3] Strategy Pattern  (2) 2020.10.25
[2] Observer Pattern  (0) 2020.10.22

댓글