본문 바로가기

카테고리 없음

Reactive Programming Pull vs Push

728x90

리액티브 프로그래밍은 Push, Pull 로 모두 구현가능하다.

 

Producer -> Cosumer 관계에서

Producer는 데이터를 생상하는 쪽

Consumer는 데이터를 받는 쪽이라고 가정한다.

 

Pull방식 : Consumer가 데이터가 필요할때 Producer에게 요청하는 방식

그리고 요청받은 데이터가 들어오면 콜백 함수나 이벤트 핸들러 등을 통해 그 데이터를 처리 하는 방식

함수의 리턴 타인이 Promise, Iterable 등이면 Pull 방식이다.

이 방식은 문제가 있다.

 

위 다이어그램은 Service(Consumer), Database(Producer) 사이의 플로우이다.

getNextAfterId로 Cosumer가 요청 (pull) 하면 Producer는 쿼리해서 response를 내려준다.

하지만 굉장히 비효율적인걸 볼수 있는데 Service가 Idle시간 만큼 놀게 된다.

그리고 Producer입장에서도 미래의 요청이 얼마나 들어올지 모르기 때문에 계속해서 새로운 요청을 기다려야한다.

 

 

 

다음은 위를 최적화한 버전이다.

getNextAfterId 가 getNextBatchAfterId로 바뀐걸 볼수 있고,

매번 한건씩 요청하는게 아니라 배치 사이즈만큼 요청하는 걸 볼수 있다.

이로인해 Producer, Consumer사이의 요청수가 줄어들었지만, 여전히 Service는 데이터베이스가 쿼리하는 동안 idle상태이다. 그리고 전과 다르게 한번의 많은 데이터를 보내기 떄문에 네트워크 비용도 증가했다.

그리고 배치 사이즈 1000이라고 가정할우 1000건 중에 몇건만 필요함에도 불구하고,1000개만큼을 전부다 읽어오고 나머지를 버려야한다. 

 

다음은 Push방식이다.

위와 같은 문제 떄문에 RXJava와 여러 reactive library 는  Pull이 아니라 Push 방식으로 구현되었고, streaming이 분산 시스템에서 사용되고 있다.

 

Service는 getStreamOfItems 호출로 Database Stream를 subscribe한다. 그리고 다 읽으면 Service 는 cancellation signal를 보내고, Database는 이 Signal를 받아 연결을 종료시킬수 있다. 이 구현으로 인해 Database는 Service를 더이상 기다리고 있을 필요가 없게 된다.

 또한, 위 다이어그램에서 볼수 있듯이 Service는 첫응답이 오기전인 Idle시간(Time to First Byte(TTFB))만 기다리면된다.

그전에는 매 요청마다 Idle 시간이 발생한걸 볼수 있다.

 

함수의 리턴 타입이 Observable, Flux, Mono 등이면 이는 Push방식이다.

 

하지만 Push 모델에서 Producer는 Consumer 의 상태를 알 수가 없다는 단점이 있다.

만약에 Consumer 가 Producer에 비해 느리다면? Producer가 Consumer에 비해 느리다면? 

그렇기 떄문에 Push 모델을 단독으로 사용하지 않고, 추가적인 기법을 사용한다.

이는 다음에 정리해보겠다.

 

Hands-on Reactive Programming in Spring 5