개발자가 되고 싶은 사람

[RxJava2 #4] WebFlux(Spring WebFlux)란?

|

WebFlux(Spring WebFlux)

1. WebFlux

  • spring framework5에서 web-flux 모듈 새롭게 추가.
  • web-flux는 server, client side에서 reactive 스타일의 application 개발을 도와주는 모듈이다.
  • spring webflux는 비동기-논블록킹 리엑티브 개발을 할 수 있다.
  • 효율적으로 동작하는 고성능 웹 어플리케이션을 개발할 수 있는 서비스간 호출이 많은 마이크로서비스 아키텍처에 적합한 프레임워크

web-flux

  • server side에서 web-flux는 2가지 방식으로 나누어진다.

    • 어노테이션 기반
    • 함수형 프로그래밍 기반
  • 아래는 web-flux 모듈의 모델이다.
  • 두방식 모두 reactive stream api, non bloking 방식으로 실행이 된다.

이미지

annotiation base model

  • 어노테이션 기반은 기존에 @RestController 어노테이션을 이용해 개발한 방식.

functional programming model

  • functional programming model은 HandlerFunctionRouterFunction 나누어 구현한다.
    • HandlerFunction - http 요청을 ServerRequest 객체를 가져와 Mono 형태로 return 합니다.
    • RouterFunction - http 요청에 대해서 HandlerFunctoin 에 routing 해줍니다. Mono 형태로 return.

2. 프로젝트에 적용된 webflux

Setup

  • Java 8
  • Spring Boot 2.1.0
  • Reactive Web
  • domain objects.
  • A data repository.
  • A handler (read: service).
  • A router.
  • RxJava2 0.1-RC42
  • junit jupiter 5.1.1

소스구조

  • 현재 프로젝트는 controller가 아닌, handler 패키지를 두어 transfer action을 수행한다.
  • 그리고 webfluxrouter에서 router를 두어 server response를 수행하고 있다.

    • router : 쉽게 생각하면 URL을 명시하는 RouterFunction과 URL과 로직을 연결하는 HandlerFunction 으로 나눌 수 있다.
src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │   └── doublechain
│   │   │   ├── RxJavaServiceApplication.java
│   │   │   ├── config
    ├── DatabaseConfig.java (비동기 rxjava-jdbc config)
    └── WebFluxRouter.java (server response config)
│   │   │   ├── service
│   │   │   │   └── transfer
│   │   │   │   ├── TransferHistoryService.java
│   │   │   │   ├── domain
│   │   │   │   │   ├── TransferHistory.java
│   │   │   │   │   └── TransferHistoryDTO.java
│   │   │   │   └── handler
│   │   │   │   └── TransferHistoryHandler.java
│   │   │   └── util
│   │   │   └── CommonUtil.java
│   │   └── resources
│   │   └── application.yml
│   └── test
│   └── java
│   └── com
│   └── doublechain
│   └── test
│   └── ServiceTest.java

Reference

  1. Spring framework 5 webflux 시작하기
  2. https://dzone.com/articles/spring-webflux-a-basic-crud-application-part-1

[RxJava2 #2] RxJava2 개략적 소개

|

RxJava2

  • RxJava1은 개발이 중단되었기 때문에 RxJava2를 사용한다.
  • reactive programming을 하기 위한 라이브러리인 Rx(reactive extensions)의 JVM판.
  • reactive streams는 java 8+에 친화적
  • 이벤트를 정의한 ‘Observable’ 인스턴스에 대해 짧은 함수를 엮은(체인) 스트림같이 이벤트 결과 데이터를 가공하는 처리를 정의, 지연실행, 비동기 콜백을 할 수 있다.
  • RxJava2.x에서는 Observable, Single, Maybe, Flowable로 클래스를 구분

RxJava1과 비교하여 달라진 RxJava2의 특징은?

1. 더이상 null을 보낼 수 없음(Observable 에서 null을 보내게 되면 바로 nullpointerException 처리 가능)

–> completable, maybe 활용 가능

2. Flowable의 추가.

  • flowable은 Backpressure를 지원하는 Observable이다.

3. Subscription의 Disposable로 변경됨.

  • reactive Streams 명세에서 source와 consumer의 상호작용을 나타내는데 subscription 이라는 이름을 사용했기 때문에, 충돌을 막기 위해 Disposable로 변경되었다.

4. Maybe가 추가됨.

  • Maybe클래스는 reduce()함수나 firstElement()함수와 같이 데이터가 발행 될 수 있거나 혹은 발행되지 않고도 완료되는 경우를 의미(값을 보낼 수도, 안 보낼 수도 있다)
  • 값이 있는 경우 onSuccess, 값이 없는 경우엔 onComplete가 호출된다.
  • 그리고 이런 리액트 프로그래밍을 하기 위해 스프링에서도 지원을 해주고 있다!

Observable

  • 옵저버블, 옵설버블 등으로 읽는다. RxJava뿐만 아니라 리액티브 프로그래밍의 기본적인 모델이다.
  • Observable = reactive stream.
  • 비동기 방식으로 전달되는 데이터 추상화.

Observer Design pattern

  • 일대다(one-to-many) 의존성을 정의한다.
  • 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.
  • 데이터 전달방식은 2가지가 있다.
  1. 주제객체에서 옵저버로 데이터를 보내는 방식(푸시 방식)
  2. 옵저버에서 주제객체의 데이터를 가져가는 방식 (풀 방식)

이미지

Flowable

  • 배압(Back pressure)를 해결 하기 위해 flowable(RxJava2부터 도입)을 했다.
  • Flowable은 Observable과 거의 비슷한데 다른 점이 back pressure이 지원된다는 점이다.
  • back pressure이란 데이터의 처리 순서를 보장해주는 로직이다.
  • Observable은 back pressure가 보장되지 않아 아이템이 순서대로 발행된다고 보장되지 않는다.
  • 하지만 Flowable은 보장된다. 따라서 Observable 보단 Flowable을 사용하는 걸 권장한다.

참고

  • backpressure? 이미지

  • Observable vs Flowable rxJava2 ? ==>
    Backpressure is when your observable (publisher) is creating more events than your subscriber can handle. So you can get subscribers missing events, or you can get a huge queue of events which just leads to out of memory eventually. Flowable takes backpressure into consideration. Observable does not. Thats it. it reminds me of a funnel which when it has too much liquid overflows. Flowable can help with not making that happen: with tremendous backpressure:

이미지

Reference

  1. http://reactivex.io/
  2. http://krksap.tistory.com/1189
  3. [RxJava2] 배압(Backpressure) 이란 무엇인가.
  4. webflux와 rxjava2-jdbc 설명
  5. Java 9의 새로운 기능 : 리 액티브 스트림
  6. https://dzone.com/articles/5-things-to-know-about-reactive-programming
  7. https://medium.com/@ggikko/rxjava-2-x-%EC%97%90-%EB%8C%80%ED%95%9C-%EC%83%9D%EA%B0%81-e11c7ca008d1
  8. flowable과 observable의 차이
  9. Reactive Thinking in Java with RxJava2(good)
  10. Observable vs Flowable rxJava2 ?

[RxJava2 #1] reactive programming 도입 배경

|

Reactive Programming 발생 배경

  • rxjava2를 보기에 앞서 잠깐 리액티브 프로그래밍의 발생 배경을 살펴보자면….

인터넷의 발전, 대용량 네트워크 트래픽 처리의 필요성 증가

  • reactive programming의 패더라임과 motivation이 발생될 수 밖에 없었던 두가지 주요 트리거.
- 하드웨어의 진화(Advancements in hardware)
- 인터넷(The internet)
  

이미지

  • 1999년도 까지만 해도 인터넷 사용자는 2억 8천만, 온라인 뱅킹은 초기단계 였다.
  • 2005년도 부터 인터넷 사용자는 10억명으로 증가, Facebook 550만명, youtube는 신생아, Netflix는 아직 비디오 스트리밍을 도입하지 않은 시기(2007)
  • 2014년도 인터넷 사용자 약 29억명이상 , Facebook 13억 사용자, 트위터 2억 7천만 사용자로 증가..!!!(^__^)
  • 이렇기 때문에 software의 scale, expectation같은 이슈사항을 직면할 수밖에 없게 되었다.

4 Reactive principles

- Responsive
- Resilient
- Scalable
- Message-driven
  

이미지

  • 현재는 인터넷 보급과 모바일 기기의 보급으로 365일 24시간 무정지의 끊김없는 빠른 서비스를 제공해야 한다.
  • 이를 위해 장애(failure)에 빨리 복구(Resilient)되고, 서비스의 수평적 확장(Scalable)이 용이해야 하며, esilient와 scalable 바탕에는 느슨한 결합(loose coupling)의 메세지 기반(message driven) 아키텍처가 기반이 되어야 한다.

message-driven

  • 메시지 드리븐 아키텍처는 반응형 응용 프로그램의 기초이다.
  • message driven application 프로그램은 이벤트 기반, 액터 기반 또는 둘의 조합일 수 있다.
1. Event-driven concurrency
2. Actor-based concurrency
   

Event-driven concurrency

  • 0개 이상의 관찰자(observers)에 의해 감시되는 이벤트를 기반으로 한다.
  • 이는 응답 대기를 차단할 필요가 없기 때문에 명령형 프로그래밍과 다르다(This is different than imperative programming because the caller doesn’t need to block waiting for a response from the invoked routine).
  • 이벤트는 특정 주소로 전달되는게 아닌, watched (or listened)를 하고 있다.

Actor-based concurrency

  • 메세지 전달 아키텍처의 확장, 메세지는 수신자에게 전달된다.
  • 메시지는 스레드 경계를 넘거나 다른 물리적 서버의 다른 액터의 메일 함으로 전달 될 수 있다.

message와 events의 주요한 차이점은, 이벤트가 발생하는 동안 메세지가 전달된다는 것이다.
0개 이상의 관찰자가 이벤트를 관찰할 수 있는 반면(observed by zero or more (0-N) observers) message의 대상은 명확하다(messages have a clear destination).

넷플릭스의 사례

  • Netflix의 tv user interface팀, 2012년 북미 인터넷 트패픽의 33%를 차지하는 엄청난 양의 데이터 처리를 해야만 했다.
  • 이 엄청난 양의 네트워크 퍼포먼스를 해결하기 위해 rest service를 옵티마이징 하고 아키텍쳐를 재설계 하게 되었다(Netflix 개발자 Jafar Husain).

이미지

  • 아키텍쳐를 재설계 하면서 고려된 키포인트 중 하나가 reactive programming model 이었다.
  • 동시성(concurrency)을 가져가면서 스레드의 안정성과 병렬처리를 개발하는것은 매우 복잡한 일이었고 그래서 이부분을 추상화시킬 프레임워크가 필요했다. 특히 기존 클라이언트 코드를 만지지 않으면서 java API를 통해 비동기 처리가 되도록 만들어야 했다. 그래서 이부분을 비동기 콜백으로 처리하기 위한 함수형 프로그래밍을 사용하는 반응형 모델을 선택하게 되었고 이 부분을 Rx Observable 모델로 가게 된 것.
  • 그래서 Jafar Husain은 jvm환경에서 ReactiveExtensions 모델이 동작되는 Java버전을 개발하게 되었으며 이것이 rxJava가 되었다.

개발 패러다임의 변화와 맞물린 reactive extensions

  • 서비스 레벨에서의 변화와 함께 web 방식의 변화도 같이 일어난다.
  • 넷플릭스의 Reactive모델 적용은 단순히 포퍼먼스의 옵티마이징이 아니였다.
  • Microservice architecture의 도입으로 비동기 통신에 기반하게 되었으며 이를 아주 쉽게 개발할수 있는 Reactive Programming의 대표적인 ReactiveExtensions가 주목받게 되었다.
  • 웹 클라이언트에도 큰 변화가 있었고, 그중에서도 javascript가 역동적으로 변하고 있다.
  • 단순 돔을 제어하고 이벤트를 처리하는 언어였지만, 이제는 컴포넌트화 되고 다양한 프레임워크들이 쏟아져 나오고 있다.
  • 서버의 변화로 클라이언트도 비동기처리에 대한 필요를 느끼고 reactiveExtensions의 rxJs가 많이 사용되고 있습니다.
  • 최근 angular2에서도 react가 채택된것을 확인해본다면 이러한 패러다임의 변화가 아주 빠르게 바뀌고 있음을 알수 있습니다.
  • 클라이언트 변화는 모바일에서도 마찬가지 이다.
  • SoundColud의 Matthias Käppler는 android에 reactiveExtensions를 적용한 rxAndroid를 내놓았으며 최근 rxSwift까지 나오면서 rxExtensions가 아주 빠르게 적용되고 바뀌어 나가고 있음을 알수있다.

Reference

  1. RxJava2를 도입하며
  2. What is Reactive Programming?
  3. [Reactive] Reactive Programming 배우는 방법
  4. reactive-history/넷플릭스

[RxJava2 #3] Reactor와 Mono, Flux이해하기

|

Reactor

  • Reactor는 Pivotal의 오픈소스 프로젝트로, JVM 위에서 동작하는 논블럭킹 애플리케이션을 만들기 위한 리액티브 라이브러리.
  • Reactor는 RxJava2와 함께 Reactive Stream의 구현체이기도 하고, Spring Framework 5부터 리액티브 프로그래밍을 위해 지원되는 라이브러리
  • Reactive Stream은 요소를 방출하거나 오류로 인해 정상적으로 완료되는 기본 rx 연산자를 사용합니다.
  • Reactor는 최소 Java8에서 동작하며 Java8의 피쳐를 잘 지원한다는 점이 있다.

Mono와 Flux

  • Reactor를 사용해 일련의 스트림을 코드로 작성하다 보면 보통 여러 스트림을 하나의 결과를 모아줄 때 Mono를 쓰고, 각각의 Mono를 합쳐서 여러 개의 값을 처리하는 Flux로 표현할 수도 있습니다.
  • 자세한 사항은 document에서 참고 가능
  • Flux와 Mono를 직접 생성하기보다는 다른 라이브러리가 제공하는 Flux와 Mono를 사용할 때가 많다. 예를 들어 스프링 5 버전에 추가된 WebClient 클래스를 사용할 때에는 WebClient가 생성하는 Mono를 이용해서 데이터를 처리한다.

1. Flux

  • Flux는 0-N개인 여러 개의 결과를 처리하는 객체
  • flux는 iterable한 데이터들을 mono로 바꿔서 나누고 합침으로 해서 subscriber에게 전달합니다. 즉 mono가 뭉치면 flux 라고 생각.
  • 여러 element를 처리

2. Mono

  • Mono는 0-1개의 결과만을 처리하기 위한 Reactor의 객체

3. just

  • just() 메서드는 이미 존재하는 값을 사용해서 Flux/Mono를 생성할 때 사용된다.
  • Mono<String> just = Mono.just("foo"); 의 경우는 just의 리턴타입이 mono이다.

Reference

  1. 사용하면서 알게 된 Reactor, 예제 코드로 살펴보기
  2. 리액티브 스트림즈, Flux, Mono

jekyll:Layout 'post' requested, cannot load such file -- bundler

|

주요 발생 에러메세지

  • Build Warning: Layout ‘post’ requested in jekyll does not exist.
  • kernel_require.rb:59:in `require’: cannot load such file – bundler (LoadError)

환경설정

  • ruby 2.5.3p105 (2018-10-18 revision 65156) [x64-mingw32]
  • Bundler version 2.0.1
  • jekyll 3.8.5

jekyll build error 발단 및 해결과정

발단1

  • 간만에,, 포스팅이 늘어남에 따라 주제별로 분류해야 겠다는 생각이 들어 폴더를 구분해 포스팅을 이동시켰다.
  • 그 다음 다시 build를 시키려는데 아래 빌드 경고가 떴다.
    Build Warning: Layout 'post' requested in jekyll does not exist.
  • 그리고 _post에 _site폴더가 생기면서 빌드된 파일이 생생되었다 ;
  • 찾아보니 내가 빌드 설정을 한 경로가 잘못되었었다;;
  • https://github.com/benbalter/wordpress-to-jekyll-exporter/issues/37

발단 2

  • 상위경로로가 다시 jekyll build를 실행해보니 이번엔 아래 에러가 떴다..
C:\workspace\wkimdev.github.io\wkimdev.github.io>jekyll build
Traceback (most recent call last):
        5: from C:/Ruby25-x64/bin/jekyll:23:in `<main>'
        4: from C:/Ruby25-x64/bin/jekyll:23:in `load'
        3: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/exe/jekyll:11:in `<top (required)>'
        2: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/lib/jekyll/plugin_manager.rb:48:in `require_from_bundler'
        1: from C:/Ruby25-x64/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
C:/Ruby25-x64/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require': cannot load such file -- bundler (LoadError)

해결과정

  • 구글링 해보니 번들파일을 설치해야 한다고 하여,, 인스톨을 진행했다.. https://github.com/jekyll/jekyll/issues/5165
Try running:

$ gem install bundler
$ bundle install
$ bundle exec jekyll serve
  • 그런데 bundle exec jekyll serve 후 다시 아래가 떠서 하라는대로 bundle insall를 다시 실행했다.
  
C:\workspace\wkimdev.github.io\wkimdev.github.io> bundle exec jekyll build
Could not find gem 'wdm (~> 0.1.0) x64-mingw32' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

C:\workspace\wkimdev.github.io\wkimdev.github.io>bundle install
Fetching gem metadata from https://rubygems.org/..............
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Fetching public_suffix 3.0.2
Installing public_suffix 3.0.2
Fetching addressable 2.5.2
Installing addressable 2.5.2
Using bundler 1.17.3
Using colorator 1.1.0
....
Fetching wdm 0.1.1
Installing wdm 0.1.1 with native extensions
Bundle complete! 7 Gemfile dependencies, 36 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.  
  • 설치가 되었다고 확인한뒤 다시 실행해보았다.
  • 잘된다~~ ^__ ㅠ
  • 단 아래와 같이 실행할 경우 다시 LoadError 에러가 떴다.
  
C:\workspace\wkimdev.github.io\wkimdev.github.io>jekyll serve
Traceback (most recent call last):
        5: from C:/Ruby25-x64/bin/jekyll:23:in `<main>'
        4: from C:/Ruby25-x64/bin/jekyll:23:in `load'
        3: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/exe/jekyll:11:in `<top (required)>'
        2: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/lib/jekyll/plugin_manager.rb:48:in `require_from_bundler'
        1: from C:/Ruby25-x64/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
C:/Ruby25-x64/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require': cannot load such file -- bundler (LoadError)  
  • 경로에 bundle install 다시 C:\{USER-DIRECTORY}\wkimdev.github.io>bundle install
  • bundle을 명시한뒤 다시 실행
    bundle exec jekyll build
        
C:\USERD\wkimdev.github.io>bundle exec jekyll build
Configuration file: C:/workspace/wkimdev.github.io/wkimdev.github.io/_config.yml
            Source: C:/workspace/wkimdev.github.io/wkimdev.github.io
       Destination: C:/workspace/wkimdev.github.io/wkimdev.github.io/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 9.265 seconds.
 Auto-regeneration: disabled. Use --watch to enable.
  • 그냥 jekyll serve라고만 하면 아래 에러가 뜨면서 되지 않는다. 메세지를 읽어보면, 문제를 해결하기 위해 bundle exec를 같이 써서 실행하라고 한다.
        
C:\workspace\wkimdev.github.io\wkimdev.github.io>jekyll serve
Traceback (most recent call last):
        10: from C:/Ruby25-x64/bin/jekyll:23:in `<main>'
         9: from C:/Ruby25-x64/bin/jekyll:23:in `load'
         8: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/exe/jekyll:11:in `<top (required)>'
         7: from C:/Ruby25-x64/lib/ruby/gems/2.5.0/gems/jekyll-3.8.5/lib/jekyll/plugin_manager.rb:50:in `require_from_bundler'
         6: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/bundler.rb:107:in `setup'
         5: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:26:in `setup'
         4: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:26:in `map'
         3: from C:/Ruby25-x64/lib/ruby/2.5.0/forwardable.rb:229:in `each'
         2: from C:/Ruby25-x64/lib/ruby/2.5.0/forwardable.rb:229:in `each'
         1: from C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:31:in `block in setup'
C:/Ruby25-x64/lib/ruby/site_ruby/2.5.0/bundler/runtime.rb:319:in `check_for_activated_spec!': You have already activated public_suffix 3.0.3, but your Gemfile requires public_suffix 3.0.2. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)
  • 다시 실행하니 잘된다~
  • bundle exec jekyll serve
        
C:\workspace\wkimdev.github.io\wkimdev.github.io>bundle exec jekyll serve
Configuration file: C:/workspace/wkimdev.github.io/wkimdev.github.io/_config.yml
            Source: C:/workspace/wkimdev.github.io/wkimdev.github.io
       Destination: C:/workspace/wkimdev.github.io/wkimdev.github.io/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 7.671 seconds.
 Auto-regeneration: enabled for 'C:/workspace/wkimdev.github.io/wkimdev.github.io'
    Server address: http://127.0.0.1:4000/
  Server running... press ctrl-c to stop.
      Regenerating: 1 file(s) changed at 2019-01-27 15:30:50
                    _posts/jekyll/test.md
                    ...done in 7.813609 seconds.

결론

  • _post경로가 아닌 ‘상위 경로’에서 jekyll build를 실행해야 한다.
  • jekyll build가 제대로 실행되면 _site폴더에 build된 포스팅들이 생긴다.

자주 포스팅을 안하면 잊어버리니까 정리해둔다.

   
  
 1. 포스팅을 작성한다
 2. _posts 상위경로로가 아래의 명령어로 build 실행한다.   
 `bundle exec jekyll build`
 3. 같은 디렉토리에서 아래 명령어로 serve 띄운다.   
 `bundle exec jekyll serve`
 4. 포스팅이 제대로 반영된게 로컬에서 확인되면 commit!