Cloud-Platform 学习——Part6 WebClient异步非阻塞请求工具


参考:

  • https://zhuanlan.zhihu.com/p/370935458?utm_id=0 - 知乎专栏
  • 在 SpringBoot 中从 RestTemplate 过渡到 WebClient:详细指南-CSDN博客

多年来,Spring 框架的 RestTemplate 一直是客户端 HTTP 访问的首选解决方案,它提供同步、阻塞 API 来以简单的方式处理 HTTP 请求。
然而,随着对非阻塞、反应式编程以更少的资源处理并发的需求不断增加,特别是在微服务架构中,RestTemplate 已经显示出其局限性。从 Spring Framework 5 开始,RestTemplate 已被标记为已弃用,Spring 团队推荐 WebClient 作为其继任者。

RestTemplate 与 WebClient 区别:

  • RestTemplate
    使用了基于每个请求对应一个线程模型(thread-per-request)的 Java ServletAPl。发送请求时,RestTemplate 为每个事件(HTTP 请求)创建一个新的线程,该线程直到 Web 客户端收到响应之前,都将一直被阻塞下去。而阻塞代码带来的问题则是,每个线程都消耗了一定的内存和 CPU 周期。这些线程将耗尽线程池或占用所有可用内存。由于频繁的 CPU上下文(线程)切换,我们还会遇到性能下降的问题
  • WebClient
    不同于 RestTemplate,WebClient 是异步的,它为每个事件创建类似于“任务“,幕后,Reactive 框架对这些 “任务” 进行排队,并仅在适当的响应可用时执行它们,等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。
    WebClient 是 Spring WebFlux 库的一部分。因此,我们还可以使用流畅的函数式 API 编写客户端代码,并将响应类型(Mono 和 Flux)作为声明来进行组合。

1.引入依赖


 <dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-webfluxartifactId>
dependency>

2.使用工具发送请求

@Test
void webclientCreate(){
	//WebClient webClient = WebClient.builder().baseUrl(BASE_URI).build();
    WebClient webClient = WebClient.create(BASE_URI);
	//普通请求
    Mono<String> mono = webClient
            .get() //get请求
            .uri(BASE_URI + "/test/get01") //WebClient.create()不带BASE_URI时就得写上请求头
            .retrieve() //获取响应体
            .bodyToMono(String.class); //格式化响应数据

	//携带请求参数——拼接请求地址栏
	Mono<String> mono01 = webClient
            .get()
            .uri(uriBuilder -> uriBuilder
                    .path("/test/get02")
                    .queryParam("pageSize",10)
                    .queryParam("pageNum",1)
                    .build())
            .retrieve()
            .bodyToMono(String.class);

	//携带请求参数——放到body中
	User user = User.builder().id("0000").password("0000").build();
    Mono<User> mono02 = webClient
            .post()
            .uri(uriBuilder -> uriBuilder.path("/test/post01").build())
            .contentType(MediaType.APPLICATION_JSON)
            .body(Mono.just(user),User.class)
//                .header()
            .retrieve()
            .bodyToMono(User.class);
	  
}

3.获取结果

    log.info("{}",mono.block());
	mono.subscribe(result -> System.out.println(result));//subscribe()方法用于订阅结果,一旦可用就会对其进行处理

4.处理错误

RestTemplate 的错误处理通过ErrorHandler接口进行,这需要单独的代码块,而 WebClient 允许直接在操作链中处理特定的 HTTP 状态,从而提供更具可读性和可维护性的方法

WebClient  webClient  = WebClient.create(); 
webClient.get() 
    .uri( "http://example.com/some-error-endpoint" ) 
    .retrieve() 
    .onStatus(HttpStatus::isError, response -> { 
        // 处理错误状态码
        return Mono.error ( new  CustomException ( "发生自定义错误。" )); 
    }) 
    .bodyToMono(String.class);

5.流数据

WebClient 还支持以数据流的形式检索响应主体,这在处理您不想一次将其全部保存在内存中的大量数据时特别有用。

WebClient  webClient  = WebClient.create(); 
webClient.get() 
    .uri( "http://example.com/stream" ) 
    .accept(MediaType.TEXT_EVENT_STREAM) // 用于服务器发送事件 (SSE)
     .retrieve() 
    .bodyToFlux(String.class) //将响应正文转换为 Flux
     .subscribe(data -> System.out.println( "Received: " + data));

在此场景中,bodyToFlux用于将响应正文转换为Flux,表示数据流。然后,该subscribe方法用于在每条数据到达时对其进行处理。这与 RestTemplate 形成鲜明对比,后者要求在处理之前将整个响应主体加载到内存中,无论大小如何。

6.重试机制

WebClient  webClient  = WebClient.builder().baseUrl( "http://example.com" ).build(); 
Mono<String> response = webClient.get() 
    .uri( "/retry-endpoint" ) 
    .retrieve() 
    .bodyToMono(String.class) 
    .retryWhen(Retry.backoff( 3 , Duration.ofSeconds( 1 )) //重试次数和退避配置
        .maxBackoff(Duration.ofSeconds( 10 ))) // 最大退避时间
    .onErrorResume(e -> Mono.just( "Fallback response" )); // 如果重试都失败则回退
 
response.subscribe(result -> System.out.println(result));

在此示例中,该retryWhen方法用于定义重试策略,指定重试次数和退避配置。如果所有重试都失败,onErrorResume则提供后备机制。

等等…

扩展:FeignClient 与 WebClient

https://springdoc.cn/spring-boot-feignclient-vs-webclient/

你可能感兴趣的:(Cloud-Platform,平台源码学习,学习)