모던 자바 인 액션 - 17장 리액티브 프로그래밍

Modern Java In Action 정리 - 리액티브 프로그래밍

  • 리액티브 프로그래밍을 정의하고 리액티브 매니패스토를 확인함
  • 애플리케이션 수준, 시스템 수준의 리액티브 프로그래밍
  • 리액티브 스트림, 자바 9 플로 API를 사용한 예제 코드
  • 널리 사용되는 리액티브 라이브러비 RxJava 소개
  • 여러 리액티브 스트림을 변환하고 합치는 RxJava 동작 살펴보기
  • 리액티브 스트림의 동작을 시각적으로 문서화하는 마블 다이어그램

모던 자바 인 액션 책을 보고 정리한 글입니다.

리액티브 매니패스토

  • 반응성
    • 리액티브 시스템은 빠르면서 일정하고 예상가능한 반응 시간을 제공한다.
  • 회복성
    • 장애가 발생하여도 시스템은 동작가능해야 한다.
    • 각 컴포넌트가 비동기적으로 작업을 다른 컴포넌트에 위임하는 등 리액티브 매니페스토는 회복성을 달성할 수 있는 다양한 기법을 제시한다.
  • 탄력성
    • 애플리케이션의 생명주기 동안 다양한 부하가 발생하는데 무거운 작업 부하가 발생할 경우 자동으로 컴포넌트에 할당된 자원 수를 늘린다.
  • 메시지 주도
    • 회복성과 탄력성을 지원하려면 약 결합, 고립, 위치 투명성등을 지원할 수 있도록 시스템을 구성하는 컴포넌트에 경계를 명확히 정의해야 한다.
    • 비동기 메시지를 전달하여 컴포넌트 끼리의 통신이 이루어진다.

애플리케이션 수준의 리액티브

  • 애플리케이션 수준의 리액티브 프로그래밍의 주요 기능은 비동기로 작업을 수행할 수 있다는 점이다.
  • 이벤트 스트림을 블록하지 않고 비동기로 처리하는 것이 최신 멀티코어 CPU의 사용률을 극대화 할 수 있다.
  • 리액티브 프레임워크나 리아브러리는 스레드를 퓨처, 액터, 일련의 콜백을 발생시키는 이벤트 루프 등과 공유하고 처리할 이벤트를 변환 및 관리한다.
  • 스레드를 다시 쪼개는 종류의 기술을 사용할 경우 메인 이벤트 루프 안에서는 절대 동작을 블락하지 않아야 한다는 전제가 필요하다.

시스템 수준의 리액티브

  • 리액티브 시스템은 여러 애플리케이션이 한 개의 일관적인, 회복가능한 플랫폼을 구성할 수 있게 해줄 뿐 아니라 이들 애플리케이션 중 하나가 실패해도 전체 시스템은 계속 운영될 수 있도록 도와주는 소프트웨어 아키텍처이다.
  • 리액티브 애플리케이션은 짧은 시간동안 연산을 수행하지만 리액티브 시스템은 애플리케이션을 조립하고 상호소통을 조절한다.
  • 리액티브 시스템의 주요 속성으로 메시지 주도를 꼽을 수 있다.
  • 리액티브 시스템에서는 수신, 발신 메시지 등과 같은 각각의 컴포넌트들이 서로 결합되지 않도록 구현해야 한다. 그래야만 시스템이 장애(회복)와 높은 부하(탄력)에서도 반응성을 유지할 수 있다.

리액티브 스트림과 플로 API

  • 리액티브 프로그래밍은 리액티브 스트림을 사용하는 프로그래밍이다. 리액티므 스트림은 잠재적으로 무한의 비동기 데이터를 순서대로 그리고 블록하지 않는 역압력을 전제해 처리하는 표준 기술이다.
  • 스트림 처리의 비동기적인 특성상 역압력 기능의 내장은 필수라는 사실을 알 수 있다. 실제 비 동기 작업이 실행되는 동안 시스템에는 암묵적으로 블록 API로 인해 역 압력이 제공되는것이다.

FLow 클래스 소개

  • Java 9에서는 리액티브 프로그래밍을 제공하는 클래스 Flow를 추가했다.
  • 이 클래스는 정적 컴포넌트 하나를 포함하고 있으며 인스턴스화 할 수 없다.
  • 리액티브 스트림 프로젝트의 표준에 따라 프로그래밍 발행-구독 모델을 지원할 수 있도록 Flow 클래스는 중첩된 인터페이스 네 개를 포함한다.
    • Publisher
    • Subscriber
    • Subscription
    • Processor
  • Publisher가 항목을 발행하면 Subscriber가 한 개 또는 여러 항목을 소비하는데 Subscription이 이 과정을 관리할 수 있도록 Flow 클래스는 관련된 인터페이스와 정적 메서드를 제공한다.
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface Publisher<T> {
  void subscribe(Subscriber<? super T> s);
}

// Subscriber 인터페이스는 Publisher가 관련 이벤트를 발행할 때 호출할 수 있도록 콜백 메서드 네 개를 정의한다.
public interface Subscriber<T> {
  void onSubscribe(Subscription s);
  void onNext(T t);
  void onError(Throwable t);
  void onComplete();
}
  • Subscriber 이벤트는 다음 프로토콜에서 정의한 순서로 지정된 메서드 호출을 통해 발행되어야 한다.
  • onSubscribe onNext* (onError onComplete)
  • onSubscribe가 반드시 먼저 호출되고, onNext는 여러번 호출 가능함을 의미한다.
1
2
3
4
public interface Subscription {
  void request(long n);
  void cancel();
}
  • Subscription 클래스는 첫번째로 Publisher에게 주어진 개수의 이벤트를 처리할 준비가 되었듬을 알릴수 있다.
  • 두번째로는 Subscription을 취소, 즉 더이상 Publisher에게 이벤트를 받지 않음을 통지한다.
  • Java 9 플로 명세서어세는 이틀 인터페이스 구현이 어떻게 서로 협력해야 하는지에 대해 설명하고 있다.
    • Publisher는 반드시 Subscription의 request 메서드에 정의된 개수 이하의 요소만 Subscription에게 전달해야 한다.
    • Subscriber는 요소를 받아 처리할 수 있음을 Publisher에게 알려야 한다.
    • Publisher와 Subscriber는 정확하게 Subscription을 공유해야 하며, 각각이 고유한 역할을 수행해야한다. 그러기위해 onSubscribe와 onNext 메서드에서 Subscriber는 request 메서드를 동기적으로 호출 할 수 있어야 한다.
  • Flow 클래스의 네 번째 이자 마지막 멤버 Processor 인터페이스는 단지 Publisher와 Subscriber를 상속 받을 뿐 아무 메서드도 추가하지 않는다.
1
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }

리액티브 라이브러리 RxJava 사용하기

  • RxJava는 넷플릭스에서 개발한 자바로 리액티브 애플리케이션을 구현하는데 사용하는 라이브러리이다.
  • RxJava 2.0 이상 버전부터 Java 9의 Flow를 지원하도록 구현되었다.
  • RxJava를 보면 Java 9에서 리액티브 당김 기반 역압력 기능(request 메서드)가 있는 Flow를 포함하는 Flowable 클래스를 확인할 수 있다.
  • 역압력은 Publisher가 너무 빠른 속도로 데이터를 발행하며 Subscriber가 이를 감당할수 없는 상황에 이르는것을 방지한다.
  • RxJava는 1000개 이하의 스트림, 마우스, 터치 이벤트 등 역압력을 적용하기 힘든 GUI 이벤트나 자주 발생하지 않는 이벤트에는 역압력을 적용하지 않을것을 권장하고 있다.