본문 바로가기
토비의봄

9. Webflux

by 이석준석이 2021. 3. 11.

1. WebClient

 

  • 비동기 nonblocking 방식으로 호출이 가능하다.
  • Mono / Flux 를 리턴한다.

 

Mono 는 Publisher 의 구현체이므로, Subscriber가 subscribe를 해야 실행된다.

  • 해당 작업은 함수의 리턴타입이 Mono<> 인 경우, WebFlux가 subscribe를 호출해주므로, 우리는 Mono 타입으로 리턴하면 된다.
  • 타입을 변경할 경우에는, map() / flatMap() 을 사용해서 변경해보자
@RestController
public static class MyController {
    @Autowired
    MyService myService;

    WebClient client = WebClient.create();

    @GetMapping("/rest")
    public Mono<String> rest(int idx) {
        Mono<ClientResponse> res = client.get().uri(URL1, idx).exchange();

        // res.map() ClientResponse 를 받아서 String 을 감싼 Mono 타입을 리턴하므로 Mono<Mono<String>> 이 된다.
        // res.flatMap() 을 사용해서 만들어야한다.
        Mono<String> body = res.flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        return body;
    }
}

 

비동기 nonblocking 관점에서보면

  • DeferredResult + AsyncTemplate 처럼 동작한다.

MyService.work 가 오래걸리는 작업이라면, netty worker Thread 를 계속 잡고있는다.

  • 이러한 경우 비동기적으로 처리를 변경해야한다.
    • CompletableFuture 로 리턴한 뒤에,
    • Mono 의 fromCompletionStage() 를 사용하여 Mono 로 변경한다.
      • // CompletableFuture 는 CompletionStage 를 상속받았기 때문

before

@GetMapping("/rest")
public Mono<String> rest(int idx) {
    return client.get().uri(URL1, idx).exchange()
            .flatMap(c -> c.bodyToMono(String.class))
            .flatMap(res1 -> client.get().uri(URL2, res1).exchange())
            .flatMap(c -> c.bodyToMono(String.class))
            .map(res2 -> myService.work(res2));
}

@Service
public static class MyService {
    public String work(String req) {
        return req + "/asyncwork";
    }
}

after

@GetMapping("/rest")
public Mono<String> rest(int idx) {
    return client.get().uri(URL1, idx).exchange()
            .flatMap(c -> c.bodyToMono(String.class))
            .flatMap(res1 -> client.get().uri(URL2, res1).exchange())
            .flatMap(c -> c.bodyToMono(String.class))
            .flatMap(res2 -> Mono.fromCompletionStage(myService.work(res2)));
}

@Service
public static class MyService {
    @Async
    public CompletableFuture<String> work(String req) {
        return CompletableFuture.completedFuture(req + "/asyncwork");
    }
}