느린 것을 걱정하지 말고, 멈춰서는 것을 걱정하라
article thumbnail
Published 2020. 10. 9. 09:50
리액터 프로젝트란 IT/리액터

리액터 프로젝트 버전 1.x

 

리액티브 스트림 스펙을 만들 때 스프링 프레임워크 팀의 개발자들은 스프링 XD 프로젝트(대용량 데이터 응용 프로그램 개발을 단순화하는 것이 목표)와 같이 처리량이 많은 데이터 처리 프레임워크가 필요했다.

 

이러한 요구를 충족시키기 위해 스프링 팀은 새로운 프로젝트를 시작했다. 프로젝트는 처음부터 비동기 논블로킹 처리를 지원하도록 설계되었다.

 

여기서 궁금한점은 비동기와 논블로킹을 지원한다는데, 부끄럽지만 나는 이 두용어를 혼용해서 사용하고 있었다. 이 둘의 차이점에 대해 설명한 글을 스택오버플로에서 찾아볼 수 있었다.

 

stackoverflow.com/questions/2625493/asynchronous-vs-non-blocking

 

asynchronous vs non-blocking

What is the difference between asynchronous and non-blocking calls? Also between blocking and synchronous calls (with examples please)?

stackoverflow.com

더보기

For example in the classic sockets API, a non-blocking socket is one that simply returns immediately with a special "would block" error message, whereas a blocking socket would have blocked. You have to use a separate function such as select or poll to find out when is a good time to retry.

예를들어 전통적인 소켓 API방식에서는, 논-블로킹 소켓은 블록될 것이라는 에러메시지를 즉각적으로 리턴하는 반면에, 블로킹 소켓은 차단할 것이다. 재시도할 적절한 시기를 찾기 위해서는 셀렉트 or 폴링같은 기능을 사용해야 할 것이다. 

 

But asynchronous sockets (as supported by Windows sockets), or the asynchronous IO pattern used in .NET, are more convenient. You call a method to start an operation, and the framework calls you back when it's done.

 

그러나 비동기 소켓 또는 .NET에서 사용하는 비동기 IO패턴은 좀 더 편리하다. 작업을 시작하는 메소드를 호출하고 해당 작업이 완료되면 프레임워크가 다시 호출될 것이다. 

 

Even here, there are basic differences. Asynchronous Win32 sockets "marshal" their results onto a specific GUI thread by passing Window messages, whereas .NET asynchronous IO is free-threaded (you don't know what thread your callback will be called on).

 

여기에서도 기본적인 차이점이 있습니다. 비동기 Win32 소켓은 Window 메시지를 전달하여 특정 GUI 스레드에 결과를 "마샬링"하는 반면 .NET 비동기 IO는 자유 스레드입니다 (콜백이 호출 될 스레드를 알 수 없음).

 

 

So they don't always mean the same thing. To distil the socket example, we could say:

  • Blocking and synchronous mean the same thing: you call the API, it hangs up the thread until it has some kind of answer and returns it to you.
  • Non-blocking means that if an answer can't be returned rapidly, the API returns immediately with an error and does nothing else. So there must be some related way to query whether the API is ready to be called (that is, to simulate a wait in an efficient way, to avoid manual polling in a tight loop).
  • Asynchronous means that the API always returns immediately, having started a "background" effort to fulfil your request, so there must be some related way to obtain the result.

그래서 그들은 항상 같은 것을 의미하지는 않습니다. 소켓 예제를 설명하기 위해 다음과 같이 말할 수 있습니다.

  • 블로킹과 동기는 같은 의미입니다. API를 호출하면 어떤 종류의 응답을받을 때까지 스레드를 끊고 반환합니다.
  • 논-블로킹란 답변을 빠르게 반환 할 수없는 경우 API가 오류와 함께 즉시 반환하고 다른 작업은 수행하지 않음을 의미합니다. 따라서 API를 호출 할 준비가되었는지 쿼리하는 관련 방법이 있어야합니다 (즉, 엄격한 루프에서 수동 폴링을 방지하기 위해 효율적인 방식으로 대기를 시뮬레이션하기 위해).
  • 비동기 란 API가 요청을 수행하기위한 "백그라운드"노력을 시작하면서 항상 즉시 반환된다는 것을 의미하므로 결과를 얻기위한 관련 방법이 있어야합니다

 

 

음,, 내가 이해한것을 정리해 보겠다.

  • 블로킹, 동기 : 특정 기능을 수행하고 해당 기능이 끝날때 까지 기다리는 것
  • 논-블로킹 : 특정 기능을 수행하고 해당기능에 대한 빠른 응답을 받을수 없을 경우 기다리는게 오류메시지 등을 반환하고, 재시도 등을 위한 폴링등의 기능이 마련되어 있는것.
  • 비동기 : 특정 기능을 수행하고 해당기능은 백그라운드에서 처리되어 결과를 얻기위한 방법이 마련되어 수행되는것.

개인적으로 공부를 진행하면서, 폴링 기능이 있었던 것이 있었던 것을 감안해 보면 해당 논블로킹 기능또한 포함되어 있음을 직감적으로 알 수 있을듯 하다.

 

 

팀은 프로젝트를 리액터 프로젝트라고 명명했다. 본질적으로 리액터 버전 1.x는 리액터 패턴, 함수형 프로그래밍 및 리액티브 프로그래밍과 같은 메시지 처리에 대한 모범 사례를 통합한 것이다. 

 

리액터 패턴은 비동기 이벤트 처리 및 동기 처리에 도움이 되는 행위 패턴입니다. 즉, 모든 이벤트가 큐에 추가되고 이벤트는 나중에 별도의 스레드에 의해 처리됩니다. 이벤트는 모든 관련 컴포넌트(이벤트 핸들러)로 발송되고 동기적으로 처리됩니다. 

 

모든 이벤트를 단계적으로 처리한다. 이와 대조적으로 이번 예제는 리액티브 프로그래밍 기법을 사용해 선언적 처리 흐름을 만듭니다. 여기에 두 가지 별도의 처리 단계가 있습니다. 게다가 코드는 잘 알려진 RxJava API와 유사해 RxJaa 사용자에게 더 익숙합니다. 어떤 점에서 리액터 1.x는 스프링 프레임워크와 잘 통합됩니다. 리액터 1.x는 메시지 처리 라이브러리 외에도 네티 애드온과 같은 다양한 추가 기능을 제공합니다. 

 

요약하자면, 리액터 1.x는 그 당시 이벤트를 빠른 속도로 처리하기에 충분했습니다. 스프링 프레임워크와의 완벽한 통합 및 네티와의 결합을 통해 비동기 및 논 블로킹 메시지 처리를 제공하는 고성능 시스템을 개바할 수 있었습니다.

그러나 리액터 1.x에는 단점이 있었는데, 단점은 다음과 같다.

  1. 배압 조절 기능이 없다.
    • 리액터 1.x의 이벤트기반 구현은 프로듀서 스레드를 차단하거나 이벤트를 생략하는 것 이외에 다른 배압 제어 방법을 제공하지 못했다.
  2. 오류처리가 상당히 복잡했다.

 

리액터 프로젝트 버전 2.x

 

리액터 1.x의 최초 공식 릴리즈가 있은 지 오래지 않아 스테판 말디니는 고성능 메시지 처리 시스템 및리액터 프로젝트 공동 리더 역할로 리액티브 스트림 특별 관심 그룹에 초대 되었다.

 

[ 스테판 말디니 - 현재는 Netflix에 재직중 ] 

 

FeignClient등도 그렇고 Netflix에서 나오는 라이브러리가 참 많다는 생각이 든다.

 

스테판 말디니와 존 브리스빈은 2015년 초에 리액터 2.x를 발표했습니다. 말디니는 “리액터 2는 리액티브 스트림의 첫 번째 시도였습니다.” 라고 말했습니다.

 

리액터 디자인의 가장 중요한 변화

  1. 이벤트버스 및 스트림 기능을 별도의 모듈로 추출
  2. 새로운 리액터 스트림 라이브러리가 리액티브 스트림 스펙을 완벽하게 준수하도록 핵심 모듈까지 다시 설계
  3. 메시지 전송을 담당하는 Reactor 객체가 EventBus로 이름이 바뀌었다.

 

다비드 카녹이라는 사람이 “High resolution and transparent production informatics(심층적이고 투명한 생산 정보학)” 에 관한 논문을 쓰는 중 이었는데, 이 논문은 리액티브 스트림, 리액티브 프로그래밍, RxJava에 대한 심층적인 연구를 다루었다.

 

말디니와 카녹은 긴밀한 협업을 통해 RxJava와 리액터 프로젝트의 아이디어와 경험을 reactive-stream-common이라는 라이브러리로 압축했다. 얼마후 이 라이브러리는 리액터 2.5의 기초가 됐고, 최종적으로 리액터 3.x가 되었다.

 

리액터 3.x는 스프링 프레임워크 5의 리액티브적인 변형을 담당했다.

 

 

리액터 프로젝트의 필수 요소

 

리액터 라이브러리는 비동기 파이프라인을 구축할 때 콜백 지옥 깊게 중첩된 코드를 생략하는 목적으로 개발되었다.

 

"리액티브 응용 프로그램을 통해 처리된 데이터는 마치 조립 라인을 통해 이동하는 것과 유사하다. 리액터는 컨베이어 벨트와 워크스테이션 역할이라고 할 수 있다.”

 

라이브러리의 기본 목표

  • 코드 가독성을 높인다.
  • 라이브러리에 의해 정의된 워크플로에 조합성(comparability)를 추가한다.

 

리액터 API는 연산자를 연결해서 사용하는 것을 권장합니다. 이를 통해 복잡하고 잠재적으로 재사용 가능한 실행 그래프를 작성할 수 있습니다. 

 

여기서 말하는 연산자는 +, -, *, / 등의 연산자를 말하는 것이 아니라, 리액터 내부에서 쓰이는 map, flatMap등 데이터의 흐름 사이에 껴서 데이터를 바꾸는 함수를 말하는 것으로 보인다.

 

이러한 실행 그래프는 실행 흐름만 정의하지만, 구독자가 실제로 구독을 만들 때까지 아무 일도 발생하지 않으므로 실제 구독을 했을 떄만 데이터 플로가 가동됩니다. 

 

리액터 프로젝트는 동시성에 좌우되지 않도록 설계됐으므로 동시성 모델을 적용하지 않습니다 (동시성이란 여러개의 스레드를 효율적으로 사용해서 CPU자원을 최대한 활용하는 것을 의미하는데, 좌우되지 않는다는 의미를 잘 모르겠다. 쓰고싶을때 쓰고 안쓰고 싶을때 안 쓴다는 의미로 해석하면 될까 싶다. ). 동시에 거의 모든 방식으로 실행 스레드를 관리할 수 있는 유용한 스케줄러 세트를 제공합니다. 

 

또한 제공된 스케줄러중 어느 것도 요구 사항에 적합하지 않다면 개발자가 로우 레벨 제어 기능을 갖춘 자체 스케줄러를 만들 수도 있습니다. 

 

 

결론, 대용량 처리를 하기위해 비동기, 논블로킹기술을 사용할 수 있도록 만든 기술로 보인다. 아직 활용을 많이 해보지 않아서 감이 잘 잡히지 않지만 계속 써보면서 감을 익혀가도록 해야 겠다.

profile

느린 것을 걱정하지 말고, 멈춰서는 것을 걱정하라

@주현태

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!