拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring Boot FeignClient 与WebClient

Spring Boot FeignClient 与WebClient

白鹭 - 2022-12-16 2334 0 2

一、概述

在本教程中,我们将比较Spring Feign——一个声明式REST 客户端和SpringWebClient一个在Spring 5 中引入的反应式Web 客户端。

2. 阻塞与非阻塞客户端

在当今的微服务生态系统中,通常需要后端服务使用HTTP 调用其他Web 服务。因此,Spring 应用程序需要一个Web 客户端来执行请求。

接下来,我们将检查阻塞Feign 客户端和非阻塞WebClient实现之间的区别。

2.1. Spring Boot 阻塞Feign 客户端

Feign 客户端是一个声明式REST 客户端,可以更轻松地编写Web 客户端。使用Feign 时,开发人员只需定义接口并相应地对其进行注释。实际的Web 客户端实现随后由Spring 在运行时提供。

在幕后,@FeignClient注释的接口生成一个基于thread-per-request 模型的同步实现因此,对于每个请求,分配的线程都会阻塞,直到它收到响应。保持多个线程存活的缺点是每个打开的线程占用内存和CPU 周期。

接下来,假设我们的服务遇到流量高峰,每秒接收数千个请求。最重要的是,每个请求都需要等待几秒钟,以便上游服务返回结果。

根据分配给托管服务器的资源和流量峰值的长度,一段时间后,所有创建的线程将开始堆积并占用所有分配的资源。因此,这一系列事件将降低服务的性能并最终导致服务崩溃。

2.2. Spring Boot 非阻塞WebClient

WebClient是Spring WebFlux 库的一部分。是Spring Reactive Framework 提供的一种非阻塞解决方案,用于解决Feign clients 等同步实现的性能瓶颈

当Feign 客户端为每个请求创建一个线程并阻塞它直到它收到响应时,WebClient执行HTTP 请求并将“等待响应”任务添加到队列中。稍后,“等待响应”任务在收到响应后从队列中执行,最终将响应传递给订阅者函数。

Reactive 框架实现了一个由Reactive Streams API 提供支持的事件驱动架构。正如我们所见,这使我们能够编写以最少数量的阻塞线程执行HTTP 请求的服务。

因此,WebClient通过使用更少的系统资源处理更多的请求,帮助我们构建在恶劣环境中始终如一地执行的服务。

3.比较例

要查看Feign 客户端和WebClient之间的区别,我们将实现两个HTTP 端点,它们都调用返回产品列表的相同慢速端点。

我们将看到,在阻塞Feign 实现的情况下,每个请求线程阻塞两秒钟,直到收到响应。

另一方面,非阻塞WebClient将立即关闭请求线程。

首先,我们需要添加三个依赖项:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

接下来,我们有慢端点定义:

@GetMapping("/slow-service-products")
private List<Product> getAllProducts() throws InterruptedException {
Thread.sleep(2000L); // delay
return Arrays.asList(
new Product("Fancy Smartphone", "A stylish phone you need"),
new Product("Cool Watch", "The only device you need"),
new Product("Smart TV", "Cristal clean images")
);
}

3.1.使用Feign 调用慢速服务

现在,让我们开始使用Feign 实现第一个端点.

第一步是定义接口并使用FeignCleint对其进行注释:

@FeignClient(value = "productsBlocking", url = "http://localhost:8080")
public interface ProductsFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/slow-service-products", produces = "application/json")
List<Product> getProductsBlocking(URI baseUrl);
}

最后,我们将使用定义的ProductsFeignClient接口来调用慢速服务:

@GetMapping("/products-blocking")
public List<Product> getProductsBlocking() {
log.info("Starting BLOCKING Controller!");
final URI uri = URI.create(getSlowServiceBaseUri());
List<Product> result = productsFeignClient.getProductsBlocking(uri);
result.forEach(product -> log.info(product.toString()));
log.info("Exiting BLOCKING Controller!");
return result;
}

接下来,让我们执行一个请求并查看日志的样子:

Starting BLOCKING Controller!
Product(title=Fancy Smartphone, description=A stylish phone you need)
Product(title=Cool Watch, description=The only device you need)
Product(title=Smart TV, description=Cristal clean images)
Exiting BLOCKING Controller!

正如预期的那样,在同步实现的情况下,请求线程正在等待接收所有产品之后,它会将它们打印到控制台并退出控制器功能,然后最终关闭请求线程。

3.2.使用WebClient调用慢速服务

其次,让我们实现一个非阻塞WebClient来调用同一个端点:

@GetMapping(value = "/products-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Product> getProductsNonBlocking() {
log.info("Starting NON-BLOCKING Controller!");
Flux<Product> productFlux = WebClient.create()
.get()
.uri(getSlowServiceBaseUri() + SLOW_SERVICE_PRODUCTS_ENDPOINT_NAME)
.retrieve()
.bodyToFlux(Product.class);
productFlux.subscribe(product -> log.info(product.toString()));
log.info("Exiting NON-BLOCKING Controller!");
return productFlux;
}

控制器函数不返回产品列表,而是返回Flux发布者并快速完成该方法在这种情况下,消费者将订阅Flux实例并在产品可用时对其进行处理。

现在,让我们再次查看日志:

Starting NON-BLOCKING Controller!
Exiting NON-BLOCKING Controller!
Product(title=Fancy Smartphone, description=A stylish phone you need)
Product(title=Cool Watch, description=The only device you need)
Product(title=Smart TV, description=Cristal clean images)

正如预期的那样,控制器功能立即完成,由此,它也完成了请求线程。一旦Product可用,订阅的函数就会处理它们。

4。结论

在本文中,我们比较了Spring 中两种编写Web 客户端的风格。

首先,我们探索了Feign 客户端,一种编写同步和阻塞Web 客户端的声明式风格。

其次,我们探索了WebClient,它支持Web 客户端的异步实现。

尽管Feign 客户端在许多情况下是一个很好的选择,并且生成的代码具有较低的认知复杂性,但WebClient的非阻塞风格在高流量高峰期间使用的系统资源要少得多。考虑到这一点,最好为这些情况选择WebClient


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *