리액티브 스트림즈 4편 - 백프레셔, 흐름을 누가 통제하는가
이번에는 백프레셔(배압제어) 개념을 조금 더 디테일하게 짚어보는 이야기를 해보려 합니다.
빠른 생산자, 느린 소비자
2편의 생산자-소비자 이야기로 돌아가 봅시다. 생산자가 소비자보다 빠르면 어떻게 될까요? 생산자가 만드는 족족 밀어내기(push)만 하면, 그 데이터들은 소비되지 못하고 계속 어딘가에 쌓이게 됩니다.

쌓이는 곳은 보통 버퍼나 큐인데, 이게 무한정 늘면 결국 메모리가 터지거나, 못 견뎌 버리기 시작하면 데이터가 유실됩니다. 핵심은 “얼마나 보낼지를 생산자가 정하는 한, 소비자는 속수무책”이라는 것에 있습니다. 그래서 누가 속도제어권을 가질 것인가가 핵심 포인트가 됩니다.
백프레셔: 받는 쪽이 흐름을 제어한다
그럼 누가 속도를 제어해야 할까요? 리액티브 스트림즈 백프레셔의 답은 “소비자”입니다. 소비자가 request(n)로 “나는 지금 n개까지 받을 수 있어”라고 알리면, 생산자는 딱 그만큼만 흘려보내줍니다. 밀어내기(push)가 아니라 당겨오기(pull)인 셈입니다.

이러면 생산 속도가 소비 속도에 묶여, 버퍼가 폭주하지 않습니다. 사실 이건 새로 발명된 개념이 아닙니다. TCP의 수신 윈도우(받는 쪽이 “이만큼만 보내라”고 알리는 흐름 제어), 메시지 큐의 prefetch 제한 같은 것도 결국 같은 메커니즘입니다. “흐름 제어”라는 오래된 문제의 한 형태인 거죠.
그래도 넘칠 땐, 무엇을 포기할까
물론 단순히 배압 제어(백프레셔) 기본적인 구성을 갖추었다고 해서 만능은 아닙니다. 소비자가 받을 양을 사실상 무한대로 요청해버리거나, 마우스 이벤트나 센서처럼 애초에 멈출 수 없는 경우들이라면 여전히 넘칠 수 있습니다. 그땐 무엇을 포기할지 골라야 합니다.
- BUFFER: 일단 쌓아둔다. 대신 메모리를 쓴다.
- DROP: 넘치는 새 값을 버린다.
- LATEST: 최신 값만 남기고 이전 건 버린다.
- ERROR: 넘쳤다고 에러를 내 알린다.
정답이 있는 게 아니라, 중간 값을 버려도 되는지·최신만 중요한지·하나도 잃으면 안 되는지를 각 문제 해결 영역에 따라 결정해야 하는 선택입니다.
참고
- 김중철 역, 실전! 스프링 5를 활용한 리액티브 프로그래밍 (원서: Hands-On Reactive Programming in Spring 5)
- Reactive Streams Specification
- 토비의 봄 TV: (1) Reactive Streams · (2) Operators (백프레셔가 명세에 어떻게 박히는지 라이브 코딩으로)
시리즈 리액티브 스트림즈 전 9편
댓글
GitHub(giscus) 댓글은 설정 완료 후 활성화됩니다.