ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3장 Blocking I/O 와 Non-Blocking I/O
    Spring/Webflux 2023. 4. 21. 21:41

    3.1  Blocking I/O

    클라이언트 PC

    본사 API : 지점 API 응답 기다리는 동안 blocking i/o 발생

    지점 API

    보완을 위해 멀티스레딩 기법 사용시

    Context Switching 으로 인한 스레드 전환 비용 발생

    컨텍스트 스위칭 과정

    PCB : Process Control Block 대기 중인 작업 저장소

     

    Context Swithcing

    01. 현재 실행되고 있는 프로세스 정보는 Cpu 의 레지스터에 저장되는데,

    프로세스들이 번갈아가며 실행되는 과정에서 PCB 에 저장된 프로세스 정보가 레지스터에 지속적으로 저장되어,

    그 값이 변경되는 것을 컨텍스트 스위칭

    02. P1 끝나고 P2 가 바로 실행되는 것이 아니라 약간의 대기시간 존재 ; PCB에 정보 저장하거나 불러오는 시간 - 이 작업이 많을 수록 성능저하

     

    참고 : 스레드의 컨텍스트 스위칭시 정보는 TCB ( Thread Control Block ) 에 저장됨

     

    JVM 의 새로운 스레드와 stack

    JVM 에서 새로운 스레드는 stack 에 할당됨

    JVM  default stack size 는 1,024kb, 64,000명 동시 접속 시 64G 의 메모리 필요

     

    Spring Boot 는 thread pool

    스레드 생성과 종료 비용을 아끼기 위해 thread pool 사용

    64,000 명 동시 접속 시 지정된 thread 갯수에서 thread switching 하며 처리

     

    3.2  Non-Blocking I/O

    non-blocking i/o 사용시 스레드가 차단되지 않음

    지점 API 응답을 대기하지 않고, 스레드가 이후 작업을 처리함

    non-blocking i/o

    non-blocking i/o 단점

    01. CPU를 많이 사용하는 작업이 포함된 경우에는 성능에 악영향

    - 대기하는 유휴 스레드 없이 모든 스레드가 일을 하므로, CPU 가 좋아야 됨

    02. 전체 과정중에 하나라도 blocking i/o 가 존재한다면, 이점이 상쇄됨

     

    요약하면

    blocking i/o 인 경우 대기 중인 스레드 작업을 저장할 메모리가 커야  함

    non-blcoking i/o 는 스레드가 유휴 작업 없이 계속 일하기 때문에 cpu 성능을 많이 사용함

     

     

    3.3  Spring Framework 에서의 Blocking I/O 와 Non-Blocking I/O

    Spring MVC : blocking i/o

     - 요청당 하나의 스레드를 사용하기 때문에, 대량 요청 처리시 과도한 스레드, CPU 대기 시간 증가, 메모리 오버헤드 발생

    Spring WebFlux : non-blocking i/o

    - Netty 같은 비동기 서버 엔진을 사용함으로서 적은 수의 스레드로 많은 수의 요청 처리, cpu/memory 효율적 사용가능

     

    Spring mvc

    https://github.com/gwagdalf/Spring-Reactive/tree/main/part1/chapter3/spring-mvc-headoffice/src/main/java/com/itvillage

    HeadOffice

     

    https://github.com/gwagdalf/Spring-Reactive/tree/main/part1/chapter3/spring-mvc-branchoffice/src/main/java/com/itvillage

    BranchOffice

     

    branchOffice 의 getBook 에서 5초 대기하면

    이를 호출한 headOffice 도 같이 5초 대기

    branch : https://github.com/gwagdalf/Spring-Reactive/blob/main/part1/chapter3/spring-mvc-branchoffice/src/main/java/com/itvillage/SpringMvcBranchOfficeController.java#L31

     

    head : https://github.com/gwagdalf/Spring-Reactive/blob/main/part1/chapter3/spring-mvc-headoffice/src/main/java/com/itvillage/SpringMvcHeadOfficeController.java#L46

     

    책 정보 1,2,3,4,5 를 얻을 때마다 5초씩 * 5 = 25초 대기

    23:00:04.022 [main] INFO - # 요청 시작 시간: 23:00:04.022648
    23:00:04.130 [http-nio-8080-exec-1] INFO - Initializing Spring DispatcherServlet 'dispatcherServlet'
    23:00:04.130 [http-nio-8080-exec-1] INFO - Initializing Servlet 'dispatcherServlet'
    23:00:04.131 [http-nio-8080-exec-1] INFO - Completed initialization in 1 ms
    23:00:09.318 [main] INFO - 23:00:09.318232: book name: IT Book1
    23:00:14.330 [main] INFO - 23:00:14.330023: book name: IT Book2
    23:00:19.339 [main] INFO - 23:00:19.339384: book name: IT Book3
    23:00:24.351 [main] INFO - 23:00:24.351177: book name: IT Book4
    23:00:29.361 [main] INFO - 23:00:29.361776: book name: IT Book5

     

    Spring WebFlux

    https://github.com/gwagdalf/Spring-Reactive/tree/main/part1/chapter3/spring-reactive-headoffice/src/main/java/com/itvillage

    HeadOffice

     

    https://github.com/gwagdalf/Spring-Reactive/tree/main/part1/chapter3/spring-reactive-branchoffice/src/main/java/com/itvillage

    BranchOffice

     

    모든 스레드가 비동기로 5초씩 기다렸다가, 한방에 결과가 쫙나옴

    brach 의 Thread.sleep(5000) 에서는 5초 대기

    하지만, head 는 비동기 이므로 5개를 non-blocking 으로  모두 보내고 결과를 subscribe 함

    23:04:39.579 [main] INFO - # 요청 시작 시간: 23:04:39.579750
    23:04:45.578 [reactor-http-nio-1] INFO - 23:04:45.578047: book name: IT Book5
    23:04:45.578 [reactor-http-nio-1] INFO - 23:04:45.578720: book name: IT Book4
    23:04:45.579 [reactor-http-nio-1] INFO - 23:04:45.579185: book name: IT Book1
    23:04:45.579 [reactor-http-nio-1] INFO - 23:04:45.579669: book name: IT Book3
    23:04:45.580 [reactor-http-nio-1] INFO - 23:04:45.580100: book name: IT Book2

     

    branchOffice Controller

    // SpringReactiveBranchOfficeController
    
        @ResponseStatus(HttpStatus.OK)
        @GetMapping("/{book-id}")
        public Mono<Book> getBook(@PathVariable("book-id") long bookId)
                throws InterruptedException {
            Thread.sleep(5000);
    
            Book book = bookMap.get(bookId);
            log.info("# book for response: {}, {}", book.getBookId(), book.getName());
            return Mono.just(book);
        }
        
    
    // application.yaml
    server:
      port: 5050

     

    headOffice Controller

    // SpringReactiveHeadOfficeApplication
    	@Bean
    	public CommandLineRunner run() {
    		return (String... args) -> {
    			log.info("# 요청 시작 시간: {}", LocalTime.now());
    
    			for (int i = 1; i <= 5; i++) {
    				int a = i;
    				this.getBook(i)
    					.subscribe(
    							book -> {
    								// 전달 받은 도서를 처리.
    								log.info("{}: book name: {}",
    										LocalTime.now(), book.getName());
    							}
    					);
    			}
    		};
    	}
    
    
    // SpringReactiveHeadOfficeController
        @ResponseStatus(HttpStatus.OK)
        @GetMapping("/{book-id}")
        public Mono<Book> getBook(@PathVariable("book-id") long bookId) {
            URI getBookUri = UriComponentsBuilder.fromUri(baseUri)
                    .path("/{book-id}")
                    .build()
                    .expand(bookId)
                    .encode()
                    .toUri(); // http://localhost:5050/v1/books/{book-id}
    
            return WebClient.create()
                    .get()
                    .uri(getBookUri)
                    .retrieve()
                    .bodyToMono(Book.class);
        }

    org.springframework.web.reactive.function.client.WebClient  사용

    응답이 Mono<Book> 이기에 .bodyToMono(Book.class) 사용

     

    Mono : Reactor 에서 지원하는 Publisher 타입 중 하나로, 단 하나의 데이터만 emit 하는 Publisher 타입입니다.

    Json형식의 응답 자체는 하나의 문자열로 구성된 단 하나의 데이터이기 때문에 Mono 를 사용하기 가장 적합

     

    3.4  Non-Blocking I/O 방식의 통신이 적합한 시스템

    Spring WebFlux 도입 시 고려사항

    1. 학습난이도
    a. 개인마다 다를 수 있지만 많은 노력과 시간, 경험이 필요
    b. 리액티브 스트림즈라는 표준 사양을 구현한 구현체를 능숙하게 다를수 있어야함
    c. 구현체 뿐만 아니라 Reactor에 대한 학습도 필요

    2. 개발인력 확보
    a. 선언형 프로그래밍 + Non-Blocking I/O 방식에 대해 숙련된 개발 인력을 확보하는 것은 상대적으로 어려움
    b. 개발인력 면이나 기술적 측면에서 위험 부담을 더 감수해야 될 가능성이 높음
    c. 새로운 기술 도입이 기술부채를 갚는 하나의 방법이 될 수 있지만 성급한 도입은 더 큰 고통을 야기할 수 있음

     

    3. Ecosystem

    Error logging, Monitoring, Circuit Braker 등 Ecosystem 도 Spring WebFlux 를 지원해야 함

     

    3.4.1  대량의 요청 트래픽이 발생하는 시스템

    a. 서버 증설이나 VM 확장등으로 트래픽 분산이 가능하지만 그만큼 높은 비용을 지불해야 함
    b. 적은 컴퓨팅 파워를 사용함으로써 저비용으로 고수준의 성능을 이끌어 내는 선택이 될 수 있음

    3.4.2  마이크로 서비스 기반 시스템

    a. 서비스간에 많은 수의 I/O가 지속적으로 발생
    b. Blocking으로 인한 응답 지연은 모든 서비스들에게 영향을 미칠 가능성이 높기 때문에 사실상 필수적(Node)

    3.4.3  스트리밍 또는 실시간 시스템

    a. 데이터 베이스 조회와 같은 일회성 연결 뿐만 아니라 끊임없이 들어오는 무한한 데이터 스트림을 전달 받아서 효율적으로 처리 가능

     

     

     

     

    'Spring > Webflux' 카테고리의 다른 글

    6장 마블 다이어그램  (1) 2023.04.22
    5장 Reactor 개요  (0) 2023.04.22
    4장 리액티브 프로그래밍을 위한 사전 지식  (0) 2023.04.22
    2장 리액티브 스트림즈  (0) 2023.04.19
    Spring Webflux 공부하기 1  (0) 2023.04.18

    댓글

Designed by Tistory.