리액티브 스트림즈 4편 - 백프레셔, 흐름을 누가 통제하는가

2022-07-17 · 엔지니어링 · 시리즈 · 리액티브 스트림즈
리액티브 스트림즈 4편 - 백프레셔, 흐름을 누가 통제하는가

이번에는 백프레셔(배압제어) 개념을 조금 더 디테일하게 짚어보는 이야기를 해보려 합니다.

빠른 생산자, 느린 소비자

2편의 생산자-소비자 이야기로 돌아가 봅시다. 생산자가 소비자보다 빠르면 어떻게 될까요? 생산자가 만드는 족족 밀어내기(push)만 하면, 그 데이터들은 소비되지 못하고 계속 어딘가에 쌓이게 됩니다.

밀어내기만 하면 초당 1000개를 만드는 생산자가 초당 10개를 처리하는 소비자에게 마구 push해 버퍼가 넘치는 그림

쌓이는 곳은 보통 버퍼나 큐인데, 이게 무한정 늘면 결국 메모리가 터지거나, 못 견뎌 버리기 시작하면 데이터가 유실됩니다. 핵심은 “얼마나 보낼지를 생산자가 정하는 한, 소비자는 속수무책”이라는 것에 있습니다. 그래서 누가 속도제어권을 가질 것인가가 핵심 포인트가 됩니다.

백프레셔: 받는 쪽이 흐름을 제어한다

그럼 누가 속도를 제어해야 할까요? 리액티브 스트림즈 백프레셔의 답은 “소비자”입니다. 소비자가 request(n)로 “나는 지금 n개까지 받을 수 있어”라고 알리면, 생산자는 딱 그만큼만 흘려보내줍니다. 밀어내기(push)가 아니라 당겨오기(pull)인 셈입니다.

백프레셔: 소비자가 request(n)으로 받을 양을 정해 당겨오고, 넘칠 땐 BUFFER·DROP·LATEST·ERROR 중 무엇을 포기할지 고르는 그림

이러면 생산 속도가 소비 속도에 묶여, 버퍼가 폭주하지 않습니다. 사실 이건 새로 발명된 개념이 아닙니다. TCP의 수신 윈도우(받는 쪽이 “이만큼만 보내라”고 알리는 흐름 제어), 메시지 큐의 prefetch 제한 같은 것도 결국 같은 메커니즘입니다. “흐름 제어”라는 오래된 문제의 한 형태인 거죠.

그래도 넘칠 땐, 무엇을 포기할까

물론 단순히 배압 제어(백프레셔) 기본적인 구성을 갖추었다고 해서 만능은 아닙니다. 소비자가 받을 양을 사실상 무한대로 요청해버리거나, 마우스 이벤트나 센서처럼 애초에 멈출 수 없는 경우들이라면 여전히 넘칠 수 있습니다. 그땐 무엇을 포기할지 골라야 합니다.

  • BUFFER: 일단 쌓아둔다. 대신 메모리를 쓴다.
  • DROP: 넘치는 새 값을 버린다.
  • LATEST: 최신 값만 남기고 이전 건 버린다.
  • ERROR: 넘쳤다고 에러를 내 알린다.

정답이 있는 게 아니라, 중간 값을 버려도 되는지·최신만 중요한지·하나도 잃으면 안 되는지를 각 문제 해결 영역에 따라 결정해야 하는 선택입니다.

참고

시리즈 리액티브 스트림즈 전 9편

  1. 01 리액티브란 무엇인가, 옵저버 패턴에서 백프레셔로
  2. 02 명세와 백프레셔, 네 개의 인터페이스
  3. 03 직접 만들어보기, pull에서 push까지 이전
  4. 04 백프레셔, 흐름을 누가 통제하는가 현재 글
  5. 05 구현체들 둘러보기 다음
  6. 06 Project Reactor 입문, Flux와 Mono
  7. 07 Spring WebFlux와 논블로킹 스택
  8. 08 끝까지 논블로킹, 그리고 한계
  9. 09 가상 스레드와 리액티브의 미래

댓글

GitHub(giscus) 댓글은 설정 완료 후 활성화됩니다.