首页 > 其他分享 >spring-webClient-响应式http客户端

spring-webClient-响应式http客户端

时间:2024-03-07 16:25:24浏览次数:23  
标签:http spring 阻塞 filter build webClient Mono WebClient

1. WebClient 简介

WebClient 是 Spring WebFlux 模块提供的一个非阻塞的基于响应式编程的进行 Http 请求的客户端工具。WebFlux 对标 SpringMvc,WebClient 相当于 RestTemplate,同时也是 Spring 官方的 Http 请求工具。

2. 传统阻塞IO模型 VS 响应式IO模型

  • 传统阻塞IO模型 RestTemplate

  Spring3.0引入了RestTemplate,SpringMVC或Struct等框架都是基于Servlet的,其底层IO模型是阻塞IO模型。采用阻塞IO模式获取输入数据。每个连接都需要独立的线程,完成数据输入、业务处理、返回。传统阻塞IO模型的问题是,当并发数很大时,就要创建大量线程,占用很大的系统资源。连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作,造成线程资源浪费。

  • 响应式IO模型 WebClient

  Spring5中引入了WebClient作为非阻塞式Reactive Http客户端。Spring社区为了解决SpringMVC的阻塞模型在高并发场景下的性能瓶颈,推出了Spring WebFlux,WebFlux底层实现是久经考验的Netty非阻塞IO通信框架。其实WebClient处理单个HTTP请求的响应时长并不比RestTemplate更快,但是它处理并发的能力更强,非阻塞的方式可以使用较少的线程以及硬件资源来处理更多的并发。

  所以响应式非阻塞IO模型的核心意义在于,提高了单位时间内有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。

  • 与RestTemplate相比,WebClient的优势
    • 非阻塞响应式IO,单位时间内有限资源下支持更高的并发量。
    • 支持使用Java8 Lambda表达式函数。
    • 支持同步、异步、Stream流式传输。

3. WebClient 依赖

WebClient 在 spring 提供的 WebFlux 中

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

4. WebClient Api

1. 创建实例

  • create() 创建实例
WebClient cli = WebClient.create();
  • create(String baseUrl) 创建实例并指定 baseURL
WebClient webClient = WebClient.create("http://localhost:8080");

2. 构建器

  • WebClient.builder().build() 使用构建器
WebClient build = WebClient.builder().build();

WebClient.builder 的额外配置:

1. uriBuilderFactory:要用作基URL的自定义uriBuilderFactory。
2. defaultUriVariables:展开URI模板时使用的默认值。
3. defaultHeader:每个请求的标头。
4. defaultCookie:每个请求的Cookie。
5. defaultRequest:消费者自定义每个请求。
6. filter:每个请求的客户端筛选器。
7. exchangeStrategies:HTTP消息读取器/写入器自定义。
8. clientConnector:HTTP客户端库设置。
9. observationRegistry:用于启用Observability支持的注册表。
10. observationConvention:一种可选的自定义约定,用于提取记录观测的元数据。
  • 创建副本

一旦构建,WebClient是不可变的。但是,您可以按如下方式克隆它并构建修改后的副本

WebClient client1 = WebClient.builder()
        .filter(filterA).filter(filterB).build();
 
WebClient client2 = client1.mutate()
        .filter(filterC).filter(filterD).build();
  • 编码器
//默认 256 kb
WebClient webClient = WebClient.builder()
        .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
        .build();

3. 获取响应

retrieve() 方法用于声明如何提取响应。

WebClient client = WebClient.create("https://example.org");
 
Mono<ResponseEntity<Person>> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .toEntity(Person.class);

或者只获得 body

WebClient client = WebClient.create("https://example.org");
 
Mono<Person> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(Person.class);

要获取解码对象流,请执行以下操作:

Flux<Quote> result = client.get()
        .uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
        .retrieve()
        .bodyToFlux(Quote.class);

bodyToFlux 和 bodyToMono:

  • bodyToFlux 方法用于将响应结果处理为 Flux 对象,Flux 是 Reactor 框架中表示包含零个、一个或多个元素的异步序列的类。这意味着响应结果可能是一个包含多个元素的流,而不是单个值。

  • bodyToMono 方法用于将响应结果处理为 Mono 对象,Mono 是 Reactor 框架中表示包含零个或一个元素的异步序列的类。这意味着响应结果是一个单个值或者没有值。

accept(MediaType... var1)

  响应数据类型

acceptCharset(Charset... var1)

  响应字符集

4. RequestBody

@RequestMapping("/body")
public void test5(){
    WebClient webClient = WebClient.create("http://localhost:8080/api/restful");

    MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
    formData.add("name", "张和");
    formData.add("color", "blue");

    Mono<Dog> dogMono = webClient.post()
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .bodyValue(formData)
            .retrieve()
            .bodyToMono(Dog.class);
    System.out.println(dogMono.block());
}

@RequestMapping("/body1")
public void test6(){
    WebClient webClient = WebClient.create("http://localhost:8080/api/json");

    Mono<Dog> mono = Mono.just(new Dog("和", "33"));

    Mono<Dog> dogMono = webClient.post()
            .contentType(MediaType.APPLICATION_JSON)
            .body(mono, Dog.class)
            .retrieve()
            .bodyToMono(Dog.class);
    System.out.println(dogMono.block());
}
  • contentType()

    设置请求参数格式

  • body()/bodyValue()

    请求参数

5. 过滤器

filter() 方法添加过滤器

public void test7(){

    Mono<Dog> mono = Mono.just(new Dog("和", "33"));

    WebClient webClient = WebClient.builder().filter(new ExchangeFilterFunction() {
        @Override
        public Mono<ClientResponse> filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) {
            ClientRequest build = ClientRequest.from(clientRequest).body(mono, Dog.class).build();
            return exchangeFunction.exchange(build);
        }
    }).build();

    Mono<Dog> dogMono = webClient.post()
            .uri("http://localhost:8080/api/json")
            .contentType(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Dog.class);
    System.out.println(dogMono.block());
}

6. 同步

WebClient 的 block() 方法可以通过在末尾阻止结果以同步方式使用

@RequestMapping("/filter")
public void test7(){

    Mono<Dog> mono = Mono.just(new Dog("和", "33"));

    WebClient webClient = WebClient.builder().filter(new ExchangeFilterFunction() {
        @Override
        public Mono<ClientResponse> filter(ClientRequest clientRequest, ExchangeFunction exchangeFunction) {
            ClientRequest build = ClientRequest.from(clientRequest).body(mono, Dog.class).build();
            return exchangeFunction.exchange(build);
        }
    }).build();

    Mono<Dog> dogMono = webClient.post()
            .uri("http://localhost:8080/api/json")
            .contentType(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(Dog.class);

    //阻塞
    System.out.println(dogMono.subscribe(dog -> {
        System.out.println(dog);
    }));

    //非阻塞
    System.out.println(dogMono.block());
}
  • subscribe() 非阻塞方式
  • block() 阻塞方式
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("loginid", "ranjunfeng");
map.add("logintype", "1");
map.add("userpassword", "feng520.");

Mono<String> dogMono = webClient.post()
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .bodyValue(map)
        .exchange()
        .flatMap(clientResponse -> {
            return clientResponse.bodyToMono(String.class).map(myResponse -> {
                MultiValueMap<String, ResponseCookie> cookies = clientResponse.cookies();
                List<ResponseCookie> ecology_jSessionid = cookies.get("ecology_JSessionid");
                //List<String> headers = response.headers().header("session-id");

                // here you build your new object with the response
                // and your header and return it.
                // return new MyNewObject(myResponse, headers);
                return ecology_jSessionid.get(0).getValue();
            });
        });

System.out.println(dogMono.block());

参考文献

官方文档
官方文档翻译版

标签:http,spring,阻塞,filter,build,webClient,Mono,WebClient
From: https://www.cnblogs.com/cnff/p/17869112.html

相关文章

  • spring-restTemplate-网络请求
    1,引言  现如今的IT项目,由服务端向外发起网络请求的场景,基本上处处可见!传统情况下,在服务端代码里访问http服务时,一般会使用JDK的HttpURLConnection或者Apache的HttpClient,不过这种方法使用起来太过繁琐,而且api使用起来非常的复杂,还得操心资源回收。  RestTempl......
  • Spring-@Bean-注解
    1.作用用于将对象存入spring的ioc容器中。@controller、@Service、@Component、@Configuration、@Repository等几个注解是一样的,都是负责将对象存入容器当中,而@Bean是用在方法上,将当前方法的返回值对象放到容器当中。2.使用@Bean一般出现在方法上面,也可用于自定义......
  • 若依集成CIM(即时推送系统)实现将服务端修改为SpringBoot+Vue前后端分离版(文末见代码
    ​ 场景若依前后端分离版本地搭建开发环境并运行项目的教程:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662 CIMGitee地址:https://gitee.com/farsunset/cimCIM项目是基于mina或者netty框架下的推送系统,我们平常使用第三方的推送SDK,如极光推送,百度......
  • Spring-@ControllerAdvice-全局处理注解
    1,@ControllerAdvice介绍@ControllerAdvice是Spring框架提供的一个注解,用于定义全局的异常处理器和全局数据绑定。它通常用于集中处理应用程序中的异常,并提供统一的异常处理逻辑。2,@ControllerAdvice的基本使用packageorg.springframework.web.bind.annotation;importj......
  • spring-JSON序列化
    1,使用场景。SpringBoot默认json为JackJson。在Controller需要返回Json数据时,我们使用了RestController,如果想对返回的数据进行一定的处理,也就是序列化对象为Json时使用。反序列化,就是当接收的参数想做一定处理,获取到处理后的数据时候。2,JsonSerializer序列化1,自定......
  • spring-HandlerMethodArgumentResolver-参数解析器
    1,参数解析器介绍  HandlerMehtodArgumentResolver,中文成为方法参数解析器,是SpringMvc组件的众多解析器之一,主要用来对Controller的方法参数进行处理。2,参数解析器的使用1,实现HandlerMethodArgumentResolver,自定义解析器publicclassMyHandlerMethodArgumentResolver......
  • Spring-@Validated-参数校验
    1.什么是javax.validationJSR303是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面(面向注解编程的时代),就可以在需要校验的时候进行校验了,在SpringBoot中已经包含在starter-web中,再其他项目中可以引用依赖,并自行......
  • Springcloud Alibaba-8-链路追踪
    1.基本概念在微服务架构中,一个请求可能需要调用很多个服务,若其中出现了错误很难去定位。链路追踪,就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等......
  • Spring反序列化失败 Type definition error: [simple type, class xxx.xxx.xxx]
    也许更好的阅读体验Typedefinitionerror:[simpletype,classcom.elm.po.CommonResult];nestedexceptioniscom.fasterxml.jackson.databind.exc.InvalidDefinitionException:Cannotconstructinstanceofcom.elm.po.CommonResult(noCreators,likedefaultconstru......
  • SpringBoot使用外部Web容器的解决方案
    SpringBoot默认内嵌了Web容器(如Tomcat、Jetty或Undertow),这使得应用可以作为独立的可执行JAR或WAR文件运行,无需外部Web容器。然而,在某些情况下,你可能想要将SpringBoot应用部署到外部的Web容器中,比如ApacheTomcat或Jetty。嵌入式的Web容器:应用可以打包成可执行的Jar。优点:简单......