Free Lines Arrow
본문 바로가기
Reactive Programing

[Reactive Programming] 개념

by skahn1215 2024. 10. 6.
728x90
반응형

Reactive Programing 의 등장

현재 소프트웨어의 아키텍처는 아래의 상황을 대응해야 한다.
리액티브는 비동기 기반으로 데이터를 처리하여 아래 상황들을 어플리케이션 관점에서 해결해준다..

  • 빅데이터 처리
  • 다양한 환경:하나의 모바일 디바이스에서 다양한 환경에 애플리케이션이 배포된다.
  • 사용패턴: 사용자는 1년 내내 항상 서비스를 이용할수 있으면 밀리초단위의 응답시간을 기대한다
  • 여기서 중요한 단어는 빅데이터 처리, 다양한 환경, 밀리초의 응답이라고 생각한다.
    이를 수용하기 위해 리액티브 프로그래밍이 등장 하였다. 

 

Blocking 방식의 문제점

  • 코드를 블록킹 방식으로 작성하면 일반적으로는 문제가 없지만
    버틀넥(병목)이 발생하면 문제가 크다.
  • 그때 Thread 를 추가하거나 서버 또는 자원을 추가해서 해결하는 방법이 있지만
    동시성 문제가 발생하고 어플리케이션에 심각한문제가 생길수 있다.

Blocking 코드에서 성능을 개선하는 방법 3가지

  • parallelize(병렬처리)
  • callback 처리
  • completableFuture

parallelize 의 문제점

  • 블록킹 방싱의 코드는 시간이 오래걸릴수 있는 데이터 베이스 요청이나, 다른 API 통신을 할때
    완료 될때까지 쓰레드가 유휴로 남아 대기상태로 있어 자원을 낭비한다.
  • 그렇기 때문에 병렬화로 모든것을 해결 할수 없다. 또한 여러 서버에서 동일한 로직들이 발생하기 때문에
    디버깅이 쉽지 않을수 있다.

 

Callback 의 문제점

  • 콜백은 여러개의 콜백을 합쳐서 개발하기 힘들다.
  • 콜백 지옥에 빠질수 있다.

CompletableFuture 의 한계

  • 복잡한 코드방식

 

Callback Vs Reactor

  • 동일한 코드를 콜백으로 작성했을때와 리액터로 작성했을때 차이를 보자
  • CompletableFuture 도 비슷하여 생략한다.

Callback

userService.getFavorites(userId, new Callback<List<String>>() { 
  public void onSuccess(List<String> list) { 
    if (list.isEmpty()) { 
      suggestionService.getSuggestions(new Callback<List<Favorite>>() {
        public void onSuccess(List<Favorite> list) { 
          UiUtils.submitOnUiThread(() -> { 
            list.stream()
                .limit(5)
                .forEach(uiList::show); 
            });
        }

        public void onError(Throwable error) { 
          UiUtils.errorPopup(error);
        }
      });
    } else {
      list.stream() 
          .limit(5)
          .forEach(favId -> favoriteService.getDetails(favId, 
            new Callback<Favorite>() {
              public void onSuccess(Favorite details) {
                UiUtils.submitOnUiThread(() -> uiList.show(details));
              }

              public void onError(Throwable error) {
                UiUtils.errorPopup(error);
              }
            }
          ));
    }
  }

  public void onError(Throwable error) {
    UiUtils.errorPopup(error);
  }
});

 

Reactor

  • 코드가 훨씬 깔끔하다.
  • 추가로 timeout 과 Error 처리를 쉽게 할수 있다.
userService.getFavorites(userId)
           .timeout(Duration.ofMillis(800)) 
           .onErrorResume(cacheService.cachedFavoritesFor(userId)) 
           .flatMap(favoriteService::getDetails) 
           .switchIfEmpty(suggestionService.getSuggestions())
           .take(5)
           .publishOn(UiUtils.uiThreadScheduler())
           .subscribe(uiList::show, UiUtils::errorPopup);

 

 

Reactive Programing 

리액티브 프로그래밍에서는 다양한 시스템과 소스에서 들어오는 데이터 항목스트림을 비동기적으로 처리한다.

비동기적으로 처리된 데이터 항목들을 합친다.

 

특징

반응성:

  • 빠르다.
  • 일정하고 예상할 수 있는 반응 시간을 제공한다.
  • 결과적으로 사용자가 기대치를 가질 수 있다.
  • 기대치를 통해 신뢰가 증가하면 신뢰할수 있는 애플리케이션이 된다.

회복성:

  • 장애가 발생해도 시스템은 반응 해야 한다.

탄력성:

  • 애플리케이션의 생명주기 동안 다양한 작업 부하를 받게 된다.
  • 리액티브는 자동으로관련 컴포넌트의 할당된 자원 수를 늘린다.

메시지 주도:

  • 비동기 메시지를 통해 통신이 이루어진다.

 

Reactive 의 수준

  • Recative의 수준에는 2가지가 있다.
    • 애플리케이션
    • 시스템

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

  • 스레드를 퓨처, 액터, 일련의 콜백을 발생시키는 이벤트 루프등과 공유하고 처리할 이벤트를 변환하고 관리한다.
  • 사용되는 기술들은 아래와 같다.
    • 이벤트, 메시지, 시그널, 이벤트 루프등을 이용하여  비동기 작업을 할수 있다.
    • 스레드 보다 가볍다.
    • 동기블록, 경쟁조건, 데드락 같은 저수준의 멀티스레드 문제를 직접 처리할 필요가 없어진다.

 

시스템 수준의 리액티브

  • 애플리케이션들중에 하나가 실패해도 전체시스템은 계속해서 운영하게 해주는 소프트웨어 아키텍처이다.
  • 애플리케이션을 조립하고 상호소통을 조절한다.
  • 사용되는 방식
    • 가장 주요한 속성중 하나는 메시지 주도 방식이다.
    • MSA 방식도 그중 하나다
  • 각각의 애플리케이션들이 독립적으로 동작하기 때문에 결합력이 낮아진다.
  • 문제가 발생하더라고 장애를 고립시켜 다른 애플리케이션들에게 영향을 주지 않는다.

 

 

Reactive stream 의 동작 방식

Publisher:

  • 데이터를 방출(게시)하는 역할을 한다.
  • 데이터를 방출하지만 subscirber가 없으면 데이터는 처리 되지 않는다.

Subscriber:

  • Publisher에서 방출된 데이터를 가져오는(구독) 역할을 한다.
  • 여기서 데이터를 처리한다.

Backpressures:

  • 역압력이라고 한다. 받을수 있는데이터의량 처리가 되었는지 등등 pub에게 알려주는 역할을 한다.
  • 이때 pub 에서 게시하는 속도가 빠르고 sub 에서 처리하는 속도가 느리면 데이터가 쌓일것이다. 
  • 그럴때 sub 은 역압력을 이용하여 pub 에게 처리가능한 데이터의 량을 알려줄수 있다.
  • 그역할을 하는것이 역압력이다.
728x90
반응형

댓글