728x90
Thread per request 모델
- 스레드 하나당 하나의 요청을 처리
- 멀티 코어 환경에서 여러개의 스레드가 여러개의 요청을 처리할 수 있다
- 단점
- 하지만 하나의 요청(스레드)이 한시간 짜리 IO를 만나게 되면 여전히 block된다.
- 자바에서 사용하는 native 스레드는 context switching(스레드간 스위칭)할 때 비용이 매우 크다
- 요청이 많아지게되면 thread per model을 효율적이지 않다.
Reactive Programming 모델
- data flow(데이터 흐름)와 propagation(전파)를 사용한다
- 완전한 non blocking 환경을 보장한다
- 기존의 방법과 다른 스레드 사용법을 이용한다
- 데이터베이스에게 read 요청을 보내면, 데이터를 가져오는 동안 스레드를 block하지 않는다
- 요청은 Publisher를 바로 반환하여 다른 Subscriber가 subscribe 할 수 있다
- Subscriber는 다른 네트워크나 데이터베이스 이벤트를 처리할 수 있다.
- 정리하면 어떤 스레드 이벤트가 생성되거나 소비되는지 특정하지 않는다
- 데이터베이스를 읽는 이벤트를 요청하고 데이터베이스 데이터를 받아오는 동안 데이터를 쓰는 이벤트를 처리할 수 있다.
- 데이터 읽기가 끝나면 다른 Publisher가 해당 이벤트를 처리할 수 있다.
Event Loop
- 서버의 비동기 프로그래밍 모델은 Event Loop를 사용한다
- Event Loop는 하나의 스레드에서 계속 돈다
- 물론 CPU 코어에 따라 여러개의 Event Loop를 돌릴 수 있다
- 이벤트를 이벤트 큐에서 순차적으로 대기하고 플랫폼에 콜백이 등록되면 바로 반환한다
- 데이터베이스 읽기 요청 이벤트를 보매녀 이벤트 큐에서 대기한다
- 자신의 차례가 되면 요청 콜백을 등록하고 바로 반환된다
- Event Loop은 콜백의 이벤트 완료 notification을 받으면 요청을 보낸 caller에게 결과를 전달한다
- event loop 모델은 node, netty nginx등에서 사용된다
WebFlux
- 기존 Thread per request모델의 어노테이션에 functional routing을 사용한다 (@Controller..)
- Reactive Stream API은 기저에 HTTP Runtime을 사용하여 상호작용할 수 있다
- WebClient라는 reactive, non blockig한 HTTP 요청 클라이언트를 제공한다
Runtime에 Thread Model 지원
- reactive 프로그램은 몇개의 스레드만을 최대한 사용한다
- 우리가 선택한 실제 Reactive Stream API runtime에 따라 스래드의 수가 다르다
- 예를들어 WEbFlux는 HttpHandler가 제공하는 Common API를 통해 다른 runtime을 사용한다
- 이 API는 여러 서버 API와 상호작용하기 위해 추상적인 메소드를 제공한다
- 그 중 netty가 기본으로 사용된다
- 아래 명령어로 JVM이 생성한 스레드를 관찰할 수 있다
Thread.getALLStackTraces() .keySet() .stream() .collect(Collectors.toList())
Reactor Netty
- React Netty가 기본 내장 서버로 사용된다.
- Netty는 요청을 처리하기 위해 많은 수의 worker thread를 생성한다
- CPU 코어 개수 이하로 생성
- 쿼드 코어 머신일 경우
- Netty는 스케일 가능한 event loop 모델을 제공한다
- Event Loop Group은 하나 이상의 Event Loop를 관리한다
- Event Loop은 반드시 계속 실행되고 있어야 한다
- 그래서 가용 코어 개수 이상의 Event Loop를 생성하지 않는 것을 추천한다
- Event Loop Group은 Event Loop에 새로 생성된 Channel들을 할당한다
- 모든 operation은 같은 스레드에 의해 실행되어야 한다
출처
728x90