입력(Input) / 출력(Output)의 약자로 운영체제에서 I/O는 일반적으로 컴퓨터 시스템이 외부 입출력 장치들과 데이터를 주고 받는 것을 의미한다.입출력(I/O) 작업은 데이터를 읽고 쓰는 작업을 의미한다.
물리적인 장치의 동작을 필요로 하기 때문에 CPU 데이터 처리 작업보다 느릴 수 있다.blocking I/O는 I/O 작업이 진행되는 동안 유저 프로세스가 자신의 작업을 중단한채, I/O가 끝날때까지 대기하는 방식을 의미한다.

Read() 를 호출해 커널에 read I/O 작업을 요청하고, read가 끝낼때 까지 application은 block이 되어 다른 작업을 하지 못한다. 이는 read I/O 가 수행될 때까지는 application이 다른 작업을 수행하지 못한다는 것을 의미한다.non-blocking I/O란 A함수가 I/O 작업을 호출했을때 I/O 작업이 완료될때 까지 A함수의 작업을 중단하지 않고 I/O 호출에 대해 즉시 리턴하고, A함수가 이어서 다른 일을 수행할 수 있도록 하는 방식을 의미한다.

동작 시나리오

의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-webflux'BookController-[server1]
package org.example.server1.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Slf4j
@RestController
public class BookController {
private final RestTemplate restTemplate = new RestTemplate();
private final WebClient webClient = WebClient.create();
@GetMapping("block/books/{id}")
public BookReadResponse getBookV1(@PathVariable String id){
// server2의 요청 url
String url = "http://localhost:8081/books/" + id + "/title";
// 주어진 URL 주소로 HTTP GET 메서드로 객체로 결과를 반환받는다
String title = restTemplate.getForObject(url, String.class);
url = "http://localhost:8081/books/" + id + "/content";
String content = restTemplate.getForObject(url, String.class);
return new BookReadResponse(title, content);
}
// Mono는 Reactor 라이브러리에서 제공하는 리액티브 스트림 API 중 하나로,
// 단일 요소 또는 없음을 비동기적으로 처리하는 컨테이너
@GetMapping("nonblock/books/{id}")
public Mono<BookReadResponse> getBookV2(@PathVariable String id) {
String url = "http://localhost:8081/books/" + id + "/title";
// HTTP 요청을 비동기적으로 처리하고 그 결과를 Mono로 받는다.
Mono<String> title = webClient
.get()
.uri(url)
.retrieve()
.bodyToMono(String.class);
url = "http://localhost:8081/books/" + id + "/content";
Mono<String> content = webClient
.get()
.uri(url)
.retrieve()
.bodyToMono(String.class);
// 두 개의 Mono가 완료될 때까지 기다린 후, 두 값(tuple.getT1()과 tuple.getT2())을 조합하여
// 새로운 BookReadResponse 객체를 생성
return Mono.zip(title, content)
.map(tuple -> new BookReadResponse(tuple.getT1(), tuple.getT2()));
}
}
동기Spring3 부터 지원비동기Mono는 Reactor 라이브러리에서 제공하는 리액티브 스트림 APIBookController-[server2]
package org.example.server2.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@Slf4j
@RestController
public class BookController {
@GetMapping("/books/{id}/title")
public Mono<String> getBookTitle(@PathVariable String id) throws InterruptedException {
Thread.sleep(5000);
return Mono.just("Title " + id);
}
@GetMapping("/books/{id}/content")
public Mono<String> getBookContent(@PathVariable String id) throws InterruptedException {
Thread.sleep(5000);
return Mono.just("Content " + id);
}
}
총 10초
Title : 5s + Content : 5s → 10s
5s