天天看点

Spring 框架里的 HTTP 调用,RestTemplate 还是 WebClient

作者:阿呜的边城
Spring 框架里的 HTTP 调用,RestTemplate 还是 WebClient

在 web 应用中,通过 HTTP 调用其他服务的 API 是很常见的操作,所以,我们的 web 应用中常常离不开网络客户端工具。

既然涉及到网络请求,就一定会有阻塞和非阻塞的选择问题。

RestTemplate

Spring 框架本身提供了 RestTemplate 作为 web 客户端抽象。在实现上,RestTemplate 使用了 Java Servlet API,该 AP I基于线程-请求模型。这意味着线程将阻塞,直到 web客户端收到响应为止。阻塞代码的问题是每个线程都会消耗了一定数量的内存和CPU周期。

如果有很多传入的请求,又有一些慢速服务,等待结果的请求迟早会堆积起来。因此,应用程序将创建许多线程,这将耗尽线程池或占用所有可用内存。由于频繁的CPU上下文切换,整个应用的性能就会下降。

WebClient

相对的,WebClient 使用 Spring Reactive 框架提供异步、无阻塞的解决方案。

RestTemplate 为每个 HTTP 调用使用线程,而 WebClient 则为每个事件创建类似于“任务”的东西。在实现中,Reactive 框架将对这些“任务”进行排队,并仅在有适当响应时执行它们。

Reactive 框架使用事件驱动的体系结构。它提供了通过响应流 API 组合异步逻辑的方法。因此,与同步阻塞的方法相比,响应式方法可以处理更多的逻辑,同时使用更少的线程和系统资源。

WebClien t是 Spring WebFlux 库的一部分。因此,我们还可以使用响应式 API 编写客户端代码,并使用响应式类型 Mono 和 Flux。

对比

我们使用代码对比一下二者的差异,先看下使用 RestTemplate:

@GetMapping("/blocking")
public List<User> getUserBlocking() {
    log.info("Starting BLOCKING Controller!");
    final String uri = getSlowServiceUri();

    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<List<User>> response = restTemplate.exchange(
      uri, HttpMethod.GET, null,
      new ParameterizedTypeReference<List<User>>(){});

    List<User> result = response.getBody();
    result.forEach(user -> log.info(user.toString()));
    log.info("Exiting BLOCKING Controller!");
    return result;
}           

再看一下使用 Sping WebFlux 里的 WebClient:

@GetMapping(value = "/non-blocking", 
            produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> getUserNonBlocking() {
    log.info("Starting NON-BLOCKING Controller!");
    Flux<User> userFlux = WebClient.create()
      .get()
      .uri(getSlowServiceUri())
      .retrieve()
      .bodyToFlux(User.class);

    userFlux.subscribe(user -> log.info(user.toString()));
    log.info("Exiting NON-BLOCKING Controller!");
    return tweetFlux;
}           

可以明显看到,RestTemplate 的使用中,代码是完全阻塞等待响应具体的 List<User>,而 WebClient 则是返回了 Flux<User>。Flux 在响应式框架中代表 1 到多个数据,所以它在异步响应成功后,实际也是获得 Iterable<User>。

小结

通过以上介绍,我们知道 RestTemplate 使用 Java Servlet API,因此是同步和阻塞的。

而 Spring WebFlux 里的 WebClient 是异步的,在等待响应返回时不会阻塞正在执行的线程。只有当响应准备就绪时,才会生成结果通知。

RestTemplate 是一个很常规的使用选择,但在某些情况下,与阻塞方法相比,非阻塞方法使用的系统资源要少得多,这个时候 WebClient 会是一个更好的选择。