리액티브 시리즈 - 1. 리액티브 프로그래밍 기본
💡 Intro
- 트래픽이 증가하고 사용자가 기대하는 요청시간은 더 빠른 응답을 원하게 되면서 리액티브 프로그래밍이라는 개념이 대두가 되기 시작했다.
- Java 진영에서는 물론이고 현재 공부하고 있는 스프링 어플리케이션에서도 리액티브 개념을 구현한 모듈이 추가되고 활용되고 있다.
- 리액티브 프로그래밍을 키워드 중심으로 알아본다. (선언형, 리액티브 스트림, pub-sub 구조, 비동기, 옵저버 패턴 등등)
- 리액티브 프로그래밍이 주요 개념이 된 이유에 대해서 고민해본다.
🌩 리액티브 프로그래밍이란?
ReativeX
ReativeX는 옵저버 스트림을 활용한 비동기 프로그래밍을 위한 API이다. 그리고 이것을 구현한 여러 구현체들이 있다.
나 같은 경우는 자바 언어를 주로 사용하는데 자바 진영에서도 리액티브 API를 구현한 RxJava가 있고, 자바9 부터 리액티브 프로그래밍을 구현할 수 있는 Flow 클래스를 제공한다.
Reactive - 무엇에 반응한다는 것인가?
Reactive는 반응하다 라는 뜻이다. 리액티브 프로그래밍은 Publisher가 발행한 데이터(요청)에 대해서 반응을 하기 때문에 리액티브 프로그래밍이라고 한다.
-
데이터가 즉각 반응을 한다는 것은 무슨 뜻일까 ?
기존 명령형 프로그램과 비교해보자. 기존 명령형 프로그래밍은 어떠한 기능을 어떻게 할 것인지에 대한 코드가 있고 이것을 하드웨어가 차례로 실행한다.
리액티브 프로그래밍은 “데이터의 흐름”을 정의하고 데이터 스트림을 그 흐름에서 선언한대로 데이터를 처리하고 구독한 subscriber가 처리된 데이터에 대한 마지막 행위를 한다. (예, 콘솔에 출력한다 등등)
조금 이해하기 어렵다면 다음 그림을 보자.
-
위 그림에서 Publisher는 Click Stream이다.
Click Stream에서 발생된 데이터의 흐름에 반응하여 마지막 subscriber가 filter된 데이터를 처리한다.
(위 그림에서 Subscriber까지 표시되지는 않았다)
-
데이터 스트림은 map → filter 파이프라인을 거치며 정제되며 마지막 데이터 스트림을 구독자가 처리하는 것이 반응형 프로그래밍이다.
-
데이터의 흐름에 따라서 데이터가 변한다면 즉각적으로 반응하여 다른 결과를 출력하게 되기 때문에 반응형 프로그래밍이라고 한다.
어떻게 반응할까?
반응형 프로그래밍에서 등장하는 키워드를 살펴보자. 가장 대표적인 반응형 비동기 프로그래밍 API인 ReactiveX 홈페이지를 참고했다.
-
옵저버 패턴
첫번째 키워드는 옵저버 패턴이다.
옵저버 패턴이란 특정 객체의 상태를 관찰하는 관찰자(observer)들을 등록하고 해당 객체에 상태변화가 있을 때마다 메서드를 통해서 객체가 자신에게 등록된 관찰자들에게 통지하는 디자인 패턴이다.
(Pub/sub 모델로도 알려져있지만 완전히 동일한 것은 아니다. 차이점은 다음 [링크](https://jistol.github.io/software engineering/2018/04/11/observer-pubsub-pattern/)를 참고해보자.
내가 자주 사용하는 언어인 자바에서도 멀티 스레드 환경에서 안정적으로 옵저버 패턴을 활용할 수 있는 인터페이스와 클래스를 제공한다.
관찰 당하는 객체는 Observable이라는 클래스를 상속하여 사용할 수 있고, 관찰하는 객체들은 Observer 인터페이스를 구현할 수 있다. synchronized 키워드를 통해서 필요시 동시성을 제어한다. (백터를 조작하는 과정 등등)
-
Iterator 패턴
두번째 키워드는 이터레이터 패턴이다.
이터레이터 패턴이란 컬렉션의 생김새나 구현방법을 노출시키지 않으면서 컬렉션 내부에 접근할 수 있는 패턴이다.
next()
와 같은 것을 호출하는 것만으로 컬렉션의 다음 element에 접근할 수 있다. 즉, 일종의 pull 방식이다.리액티브 프로그래밍에 대해서 설명할 때 이터레이터 패턴이 등장하는 이유는 BackPressure 때문이다. Publisher가 Subscriber가 감당하기 어려운 만큼의 양을 push 할 수 있으니, Subscriber가 오버플로우 되지 않도록 하는 것이 backpressure이다.
여기서 이터레이터 패턴을 사용하여 감당 가능한 만큼의 데이터를 요청(pull)하여 처리한다.
-
함수형 프로그래밍
세번째 키워드는 함수형 프로그래밍이다.
함수형 프로그래밍의 핵심은 선언형 프로그래밍이라는 것이다. 즉, 어떻게 할 것인지에 대한 구구절절한 구현이 있으면 발생 가능한 사이드 이펙트가 많으니 선언형으로 무엇을 할지를 선언하고 해당 함수를 조합하여 구현하는 것이다.
마틴 파울러는 함수형 프로그래밍을 “Functional Programming is programming without assignment statements”라고 말했다. 변수를 선언하고 대입하여 구현하는 기존 프로그래밍과 다르게 함수형 프로그래밍은 작은 문제를 해결하기 위한 여러 함수들을 조합하여 결과를 낸다.
리액티브 스트림에서는 함수형 프로그래밍을 활용하여 데이터를 가공한다. 상단의 사진에 COMBINE 부분을 보면 어떤 느낌으로 사용하는지 알 수 있을 것이다.
🌩 왜 이렇게 뜨는 개념일까?
인터넷과 모바일이 보급되면서 사용자 관련 데이터와 트래픽은 점점 더 늘어나고 있다. 그렇기 때문에 기존에 전통적인 블록킹 동기 방식의 요청 처리는 사용자가 원하는 만큼이 속도를 내지도 못하고 처리량이 현저히 낮은 문제가 있다.
-
기존의 요청 처리 방식
1개의 요청 당 1개의 스레드를 할당하여 처리한다.
하지만 요청마다 스레드를 생성하는 것은 비용이 큰 작업이기 때문에 대부분 Thread pool을 사용하여 요청을 처리한다.
특정 스레드가 할당된 요청이 블로킹 상태에 오래 머무르게 되고, thread pool에 있는 스레드를 모두 사용하면 결국 timeout이 발생하게 된다. 즉, 사용되지 않지만 점유되어 있는 스레드에 의한 비효율로 여러 요청을 빠르게 처리할 수 없다.(CPU는 일할 수 있음에도 불구하고 말이다!)
-
그렇다면 스레드 개수를 늘릴 수 없을까?
스레드 개수를 늘리면 과도한 context switching이 발생하여 오히려 CPU utilization을 떨어뜨릴 수 있다.
-
-
리액티브 스트림의 요청 처리 방식
많은 요청을 처리하기 위해서 많은 스레드를 사용하지 않는다. (주로 CPU 코어 * 2 만큼만 만들어서 사용)
이벤트 루프 기반의 비동기 논블로킹 방식을 사용하기 때문에 적은 수의 스레드로 많은 요청을 처리할 수 있으며 블로킹 상태에 스레드가 점유되어 낭비되는 일이 거의 없어진다.
비동기/논블로킹이라면 호출되는 함수의 작업 완료 여부를 책임지지 않아도 되는 비동기와 호출된 함수로 넘어간 제어권이 바로 리턴되어 다른 일을 처리할 수 있도록 하는 논블로킹의 조합이다. 따라서 스트림을 블록하지 않고 비동기로 처리하면 CPU utilization을 극대화 할 수 있다.
🌩 헷갈리는 개념들 잡고가기
Reactive Stream vs. Reactive Programming vs. Reactive System
- 위에 말한 특징들 대부분이 Reactive Stream이다.
- 이런 Reactive Stream을 사용한 프로그램이 Reactive Programming이다.
- Reactive Programming은 연관 키워드로 Event-driven, Reactive System은 Message-driven 이 많이 등장한다. 즉 전자는 한 어플리케이션 관점이고 후자는 전체 시스템 아키텍쳐의 관점으로 바라보아야한다.
- 여러 Reactive Programming 컴포넌트들을 모아놓았다고 Reactive System인 것은 아니다. Reactive Manifesto에 나온 리액티브 특징을 살린 시스템 아키텍쳐여야 Reactive System이라고 할 수 있을 것이다.
🛋 느낀 점
- 완전 새로운 개념이다 !! 마치 객체지향을 처음 공부했을 때처럼 실체가 있는 기술이 아니라 어떠한 형태의 프로그래밍 기법이니 더 개념 잡기가 어렵고 낯설었던 것 같다.
- 면접을 보거나 프로젝트를 진행할 때 사용자를 고려하면 대량의 트래픽 처리와 동시성 문제는 빠지지 않는 주제인 것 같다.
- 거의 모든 사람의 손에 모바일과 PC가 들려있으므로 리액티브 프로그래밍이 효율적인 요청처리와 동시성 측면에서도 좋은 해결책인 듯 싶다.
- 이제 리액티브 프로그래밍의 기본 개념을 잡았으니 스프링 webflux에서 어떻게 이 개념을 활용하고 적용했는지 알아볼 수 있겠다!!
- 모던 자바 인 액션과 자바의 정석에서도 자바의 리액티브 프로그래밍에 대해서 다루었다. 해당 부분을 읽고 정리해보는 것도 다음 목표이다. 관련 키워드는 Rx Java, Java Flow 등등이다. (모두 읽어보았으나 아직은 너무 어려웠다😭 추후 다시 도전)
[참고자료]