[스프링 웹플럭스] 1.2.1. HttpHandler

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-httphandler

모든 HTTP 서버는 HTTP 요청을 처리하는데 필요한 API를 제공한다. HttpHandler는 요청과 응답을 처리하는데 사용하는 메서드 하나를 정의한 간단한 컨트랙이다. 의도저으로 최소화했다. 주요 목적은 각기 다른 서버에서 리액티브 스프림 기반 API에서 HTTP 요청을 처리할 때 사용할 공통 인터페이스를 제공하는 것이다.

spring-web 모듈은 여러 서버를 지원하는 어댑터를 제공한다. 아래 테이블은 지원하는 서버와 어떤 리액티브 스트림을 사용하는지 보여준다.

Server name Server API used Reactive Streams support

Netty

Netty API

Reactor Netty

Undertow

Undertow API

spring-web: Undertow to Reactive Streams bridge

Tomcat

Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Jetty

Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[]

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

Servlet 3.1 container

Servlet 3.1 non-blocking I/O

spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

다음은 필요로 하는 의존성과, 지원하는 버전과 각 서버 코드 예제다.

Server name Group id Artifact name

Reactor Netty

io.projectreactor.ipc

reactor-netty

Undertow

io.undertow

undertow-core

Tomcat

org.apache.tomcat.embed

tomcat-embed-core

Jetty

org.eclipse.jetty

jetty-server, jetty-servlet

Reactor Netty:

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(host, port).newHandler(adapter).block();

Undertow:

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();

Tomcat:

HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);

Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();

Jetty:

HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);

Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();

ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();

핸들러를 ServletHttpHandlerAdapter를 사용해서 Servelt으로 감싸서 서블릿 3.1 컨테이너에 WAR로 배포할 수도 있다.

[스프링 웹플럭스] 1.2. 리액티브 스프링 웹

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-reactive-spring-web

spring-web 모듈은 저 수준 기반 시설과 HTTP 추상화를 제공한다. 리액티브 웹 애플리케이션을 개발하는데 필요한 클라이언트와 서버를 제공한다. 모든 공개 API는 리액티브 스트림으로 만들었고 리액터를 그것을 받쳐주는 구현체로 사용했다.

서버 기능은 두 계층으로 구성되어 있다.

  • HttpHandler와 서버 어댑터 –  HTTP 요청을 리액티브 스트림 백 프레셔로 처리하는 가장 기본적이고 공통적인 API
  • WebHandler API – 그보다 약간 높은 계층이지만 필터 체인 스타일의 프로세싱을 담고 있는 일반적인 목적의 서버 웹 API

[스프링 웹플럭스] 1.1.7. 성능 VS 확장성

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-performance

성능이란 다양한 특징과 의미가 있다. 보통, 리액티브와 논-블럭킹은 애플리케이션을 더 빠르게 만들어 주진 않는다. 몇몇 경우엔 그럴 수도 있다. 예를 들어, WebClient를 사용하여 병렬적으로 여러 리모트 콜을 실행할 수 있다. 하지만 전체적으로 볼 때 논-블럭킹 방식을 위해 더 많은 작업을 해야하기 때문에 필요한 처리 시간이 약간 증가할 수 있다.

리액티브와 논-블럭킹으로 기대할 수 있는 주요 장점은 작고, 고정된 개수의 쓰레드와 더 적은 메모리를 사용하여 확장할 수 있다는 점이다. 예측 가능한 방법으로 확장할 수 있기 때문에 부하를 받는 상황에서 애플리케이션을 더 견고하게 만들어 준다. 하지만 그러한 장점을 관찰하려면 느리고 예측 불가능한 네트워크 IO를 혼합한 어느정도의 지연이 필요하다. 바로 그 부분이 리액티브 스택이 장점을 보여주는 지점이며 그 차이는 극적일 것이다.

[스프링 웹플럭스] 1.1.6. 서버 선택하기

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-server-choice

스프링 웹플럭스는 네티, 언더토우, 톰캣, 제티와 서블릿 3.1+ 컨테이너를 지원한다. 모두 리액티브 스트림 API를 채용했다. 스프링 웹플럭스 프로그래밍 모델은 그러한 공통 API에 기반하여 만들었다.

자주 받는 질문: 톰캣이랑 제티를 어떻게 두 가지 스택에서 다 쓸 수 있는거지?

톰캣과 제티의 코어 자체는 논-블러킹이다. 하지만 서블릿 API가 그 위에 블럭킹 퍼사드를 추가했다. 서블릿 3.1 API부터 논-블럭킹 I/O를 제공하는 선택지를 제공하지만 그것을 사용하려면 다른 동기적이고 블럭킹 요소를 피하도록 주의해야 한다. 그러한 이유로 스프링의 리액티브 웹 스택은 리액티브 스트림과 연결해주는 로우 레벨 서블릿 어댑터을 가지고 있으며 서블릿 API를 노출 시키지 않아서 직접 사용할 수 없도록 했다.

스프링 부트 2는 웹플럭스를 사용할 때 네티를 기본으로 사용한다. 왜냐면 네티가 비공기, 논-블럭킹 영역에서 가장 널리 사용되고 있으며 클라이언트과 서버 모두 공유할 수 있는 리소스를 제공한다. 서블릿 3.1 논-블럭킹은 아직 많이 사용되지 않았는데 쓰기 어렵기 떄문이다. Spring WebFlux opens one practical path to adoption. (?)

스프링 부트가 기본 서버를 선택할 때 주로 바로 사용할 수 있는 것(out-of-the-box)을 선택한다. 애플리케이션은 여전히 성능 최적화 되어있고, 완전히 논-블럭킹이고 리액티브 백 프레셔를 채택한 어떤 서버든 선택할 수 있다. 스프링 부트에서 매우 손쉽게 서버를 바꿀 수 있을 것이다.