본문 바로가기
Java/모던 자바 인 액션

[모던 자바 인 액션] 리액티브 프로그래밍

by dvid 2023. 2. 4.

리액티브 프로그래밍

리액티브 프로그래밍에서는 다양한 시스템과 소스에서 들어오는 데이터 항목 스트림을 비동기적으로 처리하고 합쳐서 문제를 해결한다.
이런 애플리케이션은 발생한 데이터 항목을 바로 처리함으로 사용자에게 높은 응답성을 제공한다.

1. 리액티브 매니패스토

리액티브 매니패스토reactive manifesto(https://www.reactivemanifesto.org)에 리액티브 애플리케이션과 시스템 개발의 핵심 원칙을 정의한다.

  • 반응성(responsive)
    • 일정하고 예상할 수 있는 반응시간을 제공한다.
  • 회복성(resilient)
    • 장애가 발생해도 시스템은 반응해야 한다.
  • 탄력성(elastic)
    • 작업 부하가 발생하면 자동으로 관련 컴포넌트에 할당된 자원 수를 늘린다.
  • 메시지 주도(Message-driven)
    • 회복성과 탄력성을 지원하려면 약한 결합, 고립, 위치 투명성 등을 지원할 수 있도록 시스템을 구성하는 컴포넌트의 경계를 명확하게 정의해야 한다.
    • 비동기 메세지를 전달해 컴포넌트끼리의 통신이 이루어진다.

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

애플리케이션 수준 컴포넌트의 리액티브 프로그래밍의 주요 기능은 비동기로 작업을 수행할 수 있다는 점이다.
개발자 입장에서는 동시, 비동기 애플리케이션 구현의 추상 수준을 높일 수 있으므로 동기블록, 경쟁조건, 데드락 같은 저 수준의 멀티스레드 문제를 직접 처리할 필요가 없어지면서 비즈니스 요구사항 구현에 집중할 수
있다.

스레드를 다시 쪼개는 종류의 기술을 이용할 때는 메인이벤트 루프 안에서는 절대 동작을 블럭하지 않아야 한다.
CPU 작업과 I/O 작업을 분리하면 조금 더 정밀하게 풀의 크기 등을 설정할 수 있고 두 종류의 작업의 성능을 관찰할 수 있다.

1.2 시스템 수준의 리액티브

리액티브 시스템은 여러 애플리케이션이 한 개의 일관적인, 회복할 수 있는 플랫폼을 구성할 수 있게 해 줄 뿐 아니라 이들 애플리케이션 중 하나가 실패해도 전체 시스템은 계속 운영될 수 있도록 도와주는 소프트웨어
아키텍처이다.
리액티브 시스템의 주요 속성으로 메시지 주도를 꼽을 수 있다.

메시지는 정의된 목적지 하나를 향하는 반면, 이벤트는 관련 이벤트를 관찰하도록 등록한 컴포넌트가 수신한다는 점이 다르다.
리액티브 시스템에서는 수신자와 발신자가 각각 수신메시지, 발신메시지와 결합하지 않도록 이들 메시지를 비동기로 처리해야 한다.
각 컴포넌트를 완전히 고립하려면 이들이 결합되지 않도록 해야 하며 그래야만 시스템이 회복성탄력성을 가져 반응성을 유지할 수 있다.

2. 리액티브 스트림과 FLOW API

리액티브 프로그래밍은 리액티브 스트림을 사용하는 프로그래밍이다.
리액티브 스트림은 잠재적으로 무한의 비동기 데이터를 순서대로 그래고 블록하지 않는 역압력을 전재해 처리하는 표준 기술이다.
역압력은 발행-구독 프로토콜에서 이벤트 스트림의 구독자가 발행자가 이벤트를 제공하는 속도보다 느린 속도로 이벤트를 소비하며 문제가 발생하지 않도록 보장하는 장치이다.

비동기 API를 이용하면 하드웨어 사용률을 극대화할 수 있지만 다른 느린 다운스트림 컴포넌트에 너무 큰 부하를 줄 가능성도 생긴다.
따라서 이런 상황을 수신자가 스레드를 블록하지 않고도 데이터 수신자가 처리할 수 없을 만큼 많은 데이터를 받는 일을 방지하는 프로토콜을 제공한다.

2.1 Flow 클래스 소개

자바 9에서는 리액티브 프로그래밍을 제공하는 클래스 java.util.concurrent.Flow를 추가했다.
리액티브 스트림 프로젝트의 표준에 따라 프로그래밍 발행-구독 모델을 지원할 수 있도록 Flow 클래스는 중첩된 인터페이스 네 개를 포함한다.

  • Publisher
  • Subscriber
  • Subscription
  • Processor

Publisher가 항목을 발행하면 Subscriber가 한 개씩 또는 한 번에 여러 항목을 소비하는데 Subscription이 이 과정을 관리할 수 있도록 Flow 클래스는 관련된 인터페이 스롸 정적
메서드를 제공한다.
Publisher는 수많은 일련의 이벤트를 제공할 수 있지만, Subscriber의 요구사항에 따라 역압력 기법에 의해 이벤트 제공속도가 제한된다.
SubscriberPublisher가 발행한 이벤트의 리스너로 자신을 등록할 수 있다.
SubscriptionPublisherSubscriber 사이의 제어흐름, 역압력을 관리한다.


@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();
}

이들 이벤트는 다음 프로토콜에서 정의한 순서로 지정된 메서드 호출을 통해 발행되어야 한다.

onSubscribe onNext*(onError|onComplete)?

onSubscribe 메서드가 항상 처음 호출되고 이어서 onNext가 여러 번 호출될 수 있음을 의미한다.

SubscriberPublisher에 자신을 등록할 때 Publisher는 처음으로 onSubscribe 메서드를 호출해 Subscription 객체를 전달한다.
Subscription은 첫 번째 메서드로 Publisher에게 주어진 개수의 이벤트를 처리할 준비가 되었음을 알릴 수 있다.
두 번째 메서드는 Subscription을 취소, 즉 Publisher에게 더 이상 이벤트를 받지 않음을 통지한다.

public interface Subscription {
    void request(long n);
    void cancel();
}

요약

  • Publisher는 반드시 Subscriptionrequest 메서드에 정의된 개수 이하의 요소만 Subscriber에 전달해야 한다.
  • Subscriber는 요소를 받아 처리할 수 있음을 Publisher에 알려야 한다.
  • PublisherSubscriber는 정확하게 Subscription을 공유해야 하며 각각이 고유한 역할을 수행해야 한다.

17-3 Flow API를 사용하는 리액티브 애플리케이션의 생명주기

 

Flow 클래스의 네 번째 멤버 Processor 인터페이스는 단지 PublisherSubscriber를 상속받을 뿐 아무 메서드도 추가하지 않는다.

public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }

이 인터페이스는 리액티브 스트림에서 처리하는 이벤트의 변환단계를 나타낸다.
Processor의 목적은 Publisher를 구독한 다음 수신한 데이터를 가공해 다시 제공하는 것이다.

자바 9 Flow API / 리액티브 스트림 API에서는 Subscriber 인터페이스의 모든 메서드 구현이 Publisher를 블록하지 않도록 강제하지만 이들 메서드가 이벤트를 동기적으로 처리해야 하는지 비동기적으로 처리해야 하는지 지정하지 않는다.
하지만 이들 인터페이스에 정의된 모든 메서드는 void를 반환하므로 온전히 비동기 방식으로 이들 메서드를 구현할 수 있다.

2.4 자바는 왜 Flow API 구현을 제공하지 않는가?

자바 9의 Flow API는 구현을 제공하지 않는다.
API를 만들 당시 Akka, RxJava 등 다양한 리액티브 스트림의 자바 코드 라이브러리가 이미 존재했기 때문이다.
이들 라이브러리는 독립적으로 개발되었고 서로 다른 이름규칙과 API를 사용했다. 자바 9의 표준화 과정에서 기존처럼 자신만의 방법이 아닌 java.util.concurrnet.Flow의 인터페이스를 기반으로 리액티브 개념을 구현하도록 진화했다.
이 표준화 작업 덕분에 다양한 라이브러리가 쉽게 협력할 수 있게 되었다.

댓글