[Reactive Programming] Reactive Streams

DaeHoon·2023년 6월 17일
0

Reactive manifesto

  • 소프트웨어 아키텍쳐에 대한 선언문
  • Reactive system의 특성을 강조하고 구축에
    필요한 가이드라인 제공
  • 4가지의 핵심 가치를 제시

Responsive(응답성)

요구사항

  • 문제를 신속하게 탐지하고 효과적으로 대처
  • 신속하고 일관성 있는 응답 시간 제공
  • 신뢰할 수 있는 상한선을 설정하여 일관된 서비
    스 품질을 제공

결과

  • 가능한 한 즉각적으로 응답
  • 사용자의 편의성과 유용성의 기초
  • 오류 처리를 단순화
  • 일반 사용자에게 신뢰를 조성하고, 새로운 상호작용 촉진

Resilient(복원력)

요구사항

  • 복제, 봉쇄, 격리, 위임에 의해 실현
  • 장애는 각각의 구성 요소에 포함 (봉쇄)
  • 구성 요소들은 서로 분리 (격리)
  • 복구 프로세스는 다른(외부의) 구성 요소에 위임 (위임)
  • 필요한 경우 복제를 통해 고가용성이 보장 (복제)

결과

  • 장애에 직면하더라도 응답성을 유지
  • 시스템이 부분적으로 고장이 나더라도, 전체 시스템을 위험하게 하지 않고 복구 할 수 있도록 보장
    -구성 요소의 클라이언트는 장애를 처리하는데에 압박을 받지 않습니다.

Elastic(유연성)

요구사항

  • 경쟁하는 지점이나 중앙 집중적인 병목 현상이 존재하지 않도록 설계
  • 구성 요소를 샤딩하거나 복제하여 입력을 분산
  • 실시간 성능을 측정하는 도구를 제공
  • 응답성 있고 예측 가능한 규모 확장 알고리즘을 지원

결과

  • 작업량이 변화하더라도 응답성을 유지
  • 입력 속도의 변화에 따라 이러한 입력에 할당된 자원을 증가시키거나 감소
  • 상품 및 소프트웨어 플랫폼에 비용 효율이 높은 방식으로 유연성 을 제공

Message Driven(메시지 기반)

요구사항

  • 비동기 메시지 전달에 의존
  • 명시적인 메시지 전달
  • 위치 투명 메시징을 통신 수단으로 사용
  • 논블로킹 통신

결과

  • 구성 요소 사이에서 느슨한 결합, 격리, 위치 투명성을 보장하는 경계를 형성. 이 경계는 장애를 메시지로 지정하는 수단을 제공
  • 시스템에 메시지 큐를 생성하고, 모니터링하며 필요시 배압을 적용
  • 유연성을 부여하고, 부하 관리와 흐름제어를 가능하게
  • 단일 호스트든 클러스터를 가로지르든 동일한 구성과 의미를 갖고 장애를 관리
  • 수신자가 활성화가 되어 있을 때만 자원을 소비할수 있기 때문에 시스템 부하를 억제
  1. 비동기 통신: 구성 요소는 서로 비동기적으로 메시지를 주고 받으며, 독립적인 실행을 보장한다.
  2. 메시지 큐: 메시지 큐를 생성하고 배압을 적용하여 부하를 관리하고 흐름을 제어한다.
  3. 복원력: 구성 요소 사이에 경계를 형성하여 직접적인 장애의 전파를 막고 장애를 메시지로 지정해서 위치와 상관 없이 동일하게 장애를 관리한다.
  4. 탄력성: 구성 요소 사이에 경계를 형성하여 각각의 구성 요소를 독립적으로 확장 가능

Reactive manifesto 정리

  • 핵심 가치: 가능한 한 즉각적으로 응답
  • 첫 번째 형태 1: 장애에 직면하더라도 응답성을 유지
  • 두 번째 형태: 작업량이 변화하더라도 응답성을 유지
  • 방법: 비동기 non-blocking 기반의 메시지 큐를 사용해서 통신한다.

Reactive programming

일반적인 서비스

  • 일반적인 서비스에서 구성 요소 혹은 객체는 다른 객체를 직접 호출하고 데이터를 받는다. (동기적으로 리턴값을 받음)
  • 이 과정에서 경계가 무너지고 구성 요소의 독립적인 실행이 보장되지 않으며 복원력, 유연성 모두 위협을 받게 된다. (Callee가 리턴이 될 때 까지 Caller는 기다려야 하기 때문)
  • 여기에 Reactive manifesto를 적용한다면?

동기 stream

  • callee는 caller에게 응답이 아닌 stream을 제공한다.
  • callee는 각각의 값을 stream을 통해서 전달한다.
  • caller는 해당 stream을 collect하여 이를 처리한다.

요구사항 체크

  • 비동기 통신: 구성 요소는 서로 비동기적으로 메시지를 주고 받으며, 독립적인 실행을 보장한다.
    • caller (A)는 collect를 통해서 값을 조회해야 한다.
    • caller (A)와 callee(B)는 동기적으로 동작한다.
  • 메시지 큐: 메시지 큐를 생성하고 배압을 적용하여 부하를 관리하고 흐름을 제어한다.
    • stream이 메시지 큐의 역할을 하지만, 부하를 관리할 수 없다.

비동기 future

  • callee는 caller에게 응답이 아닌 future를 제공한다.
  • callee는 각각의 값을 future를 통해서 전달한다
  • caller는 해당 future를 chaining 하여 이를 처리한다

요구사항 체크

  • 비동기 통신: 구성 요소는 서로 비동기적으로 메시지를 주고 받으며, 독립적인 실행을 보장한다.
    • future 객체를 통해 서로 비동기적인 메시지를 주고 받으며, 독립적인 실행을 한다. 즉, caller와 callee는 비동기적으로 동작한다.
  • 메시지 큐: 메시지 큐를 생성하고 배압을 적용하여 부하를 관리하고 흐름을 제어한다.
    • future는 메시지 큐의 역할을 할 수 없고, 부하를 관리할 수 없다. 배압도 적용할 수 없다

Reactive stream

  • callee는 caller에게 응답이 아닌 publisher를 제공한다.
  • callee는 각각의 값을 publisher를 통해서 전달한다.
  • caller는 해당 publisher를 subscribe하거나 다른 caller에게 전달한다.
  • caller는 subscriber를 등록하여 backpressure를 조절하여 처리 가능한만큼 전달 받는다.

요구사항 체크

  • 비동기 통신: 구성 요소는 서로 비동기적으로 메시지를 주고 받으며, 독립적인 실행을 보장한다.
    • callee는 publisher를 반환하고 caller는 subscriber를 등록한다. 이 과정에서 caller와 callee는 비동기적으로 동작한다.
  • 메시지 큐: 메시지 큐를 생성하고 배압을 적용하여 부하를 관리하고 흐름을 제어한다.
    • publisher는 메시지 큐를 생성해서 부하를 관리하고 흐름을 제어한다. back-pressure를 조절할 수 있는 수단을 제공한다.

정리하자면

  • 비동기 데이터 stream을 사용하는 패러다임
  • 모든 것이 이벤트로 구성되고 이벤트를 통해서 전파되어야 한다
    - event-driven 해야 한다
    - 데이터의 전달, 에러, 완료 까지 모두 이벤트로 취급
  • Reactive manifesto의 Responsive,Resilient, Elastic, Message Driven 까지 모두 해당됨

Reactive Stream

  • Publisher: 데이터 혹은 이벤트를 제공
  • Subscriber: 데이터 혹은 이벤트를 제공받음
  • Subscription: 데이터 흐름을 조절
  • Flow 클래스를 기준으로 설명함

Publisher

@FunctionalInterface
  public static interface Publisher<T> {
	public void subscribe(Subscriber<? super T> subscriber);
}
  • Publisher가 항목을 발행하면 Subscriber가 한 개씩 또는 한 번에 여러 항목을 소비하는데 Subscription이 이 과정을 관리할 수 있도록 Flow 클래스는 관련된 인터페이스와 정적 메서드를 제공한다.
  • Publisher는 수많은 일련의 이벤트를 제공할 수 있지만 Subscriber의 요구사항에 따라 역압력 기법에 의해 이벤트 제공 속도가 제한되고, Subscription은 Publisher와 Subscriber 사이의 제어 흐름, 역압력을 관리한다

Subscriber

public static interface Subscriber<T> {
  public void onSubscribe(Subscription subscription);
  public void onNext(T item);
  public void onError(Throwable throwable);
  public void onComplete();
}
  • Subscriber 인터페이스는 Publisher가 관련 이벤트를 발행할 때 호출할 수 있도록 콜백 메서드 네 개를 정의한다.
  • Subscriber가 Publisher에 자신을 등록할 때 Publisher는 처음으로 onSubscribe 메서드를 호출해 Subscription 객체를 전달하고 이어서 onNext가 여러 번 호출될 수 있다.
  • 이벤트 스트림은 계속 지속되거나 onComplete 콜백을 통해 더 이상의 데이터가 없고 종료됨을 알릴 수 있으며, Pushlisher 장애가 발생했을 때는 onError를 호출할 수 있다.

Subscription

public static interface Subscription {
  	public void request(long n);
	public void cancel();
}
  • Publisher에게 주어진 개수의 이벤트를 처리할 준비가 되었음을 request 메서드를 통해 알릴 수 있고, cancel 메서드는 Subscription을 취소, 즉 Publisher에게 더 이상 이벤트를 받지 않음을 통지한다.
  • back-pressure를 조절할 수 있는 request 함수
  • Publisher가 onNext를 통해서 값을 전달하는 것을 취소할 수 있는 cancel 함수

Flow 명세서

  • Publisher는 반드시 Subscription의 request 메서드에 정의된 개수 이하의 요소만 Subscriber에 전달해야 한다.

  • Publisher는 지정된 개수보다 적은 수의 요소를 onNext로 전달할 수 있으며 동작이 성공적으로 끝났으면 onComplete를 호출하고 문제가 발생하면 onError를 호출해 Subscription을 종료할 수 있다.

  • Subscriber는 요소를 받아 처리할 수 있음을 Publisher에 알려야 한다. 이런 방식으로 Subscriber는 Publisher에 역압력을 행사할 수 있고 너무 많은 요소를 받는 일을 피할 수 있다.

  • Subscriber는 Subscription.request() 메서드 호출이 없이도 언제든 종료 시그널을 받을 준비가 되어있어야 하며 Subscription.cancel()이 호출된 이후에라도 한 개 이상의 onNext를 받을 준비가 되어있어야 한다.

  • Publisher와 Subscriber는 정확하게 Subscription을 공유해야 하며 각각이 고유한 역할을 수행해야 한다. 그러려면 onSubscribe와 onNext 메서드에서 Subscriber는 request 메서드를 동기적으로 호출할 수 있어야 한다.

profile
평범한 백엔드 개발자

0개의 댓글