• Spring WebFlux 入门


    1. WebFlux介绍

    Spring WebFlux 是 Spring Framework 5.0中引入的新的响应式web框架。与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。

    Spring WebFlux 用于创建基于事件循环执行模型的完全异步且非阻塞的应用程序。

    (PS:所谓异步非阻塞是针对服务端而言的,是说服务端可以充分利用CPU资源去做更多事情,这与客户端无关,客户端该怎么请求还是怎么请求。)

    Reactive Streams是一套用于构建高吞吐量、低延迟应用的规范。而Reactor项目是基于这套规范的实现,它是一个完全非阻塞的基础,且支持背压。Spring WebFlux基于Reactor实现了完全异步非阻塞的一套web框架,是一套响应式堆栈。

    【spring-webmvc + Servlet + Tomcat】响应式的、异步非阻塞的

    【spring-webflux + Reactor + Netty】命令式的、同步阻塞的

    2. Spring WebFlux Framework

    Spring WebFlux有两种风格:功能性和基于注释的。基于注释的与Spring MVC非常相近。例如:

     1 @RestController
     2 @RequestMapping("/users")
     3 public class MyRestController {
     4 
     5     @GetMapping("/{user}")
     6     public Mono<User> getUser(@PathVariable Long user) {
     7     // ...
     8     }
     9 
    10     @GetMapping("/{user}/customers")
    11     public Flux<Customer> getUserCustomers(@PathVariable Long user) {
    12     // ...
    13     }
    14     
    15     @DeleteMapping("/{user}")
    16     public Mono<User> deleteUser(@PathVariable Long user) {
    17     // ...
    18     }
    19 } 

    与之等价,也可以这样写:

     1 @Configuration
     2 public class RoutingConfiguration {
     3     @Bean
     4     public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
     5         return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
     6             .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
     7             .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
     8     }
     9 }
    10 
    11 @Component
    12 public class UserHandler {
    13     public Mono<ServerResponse> getUser(ServerRequest request) {
    14     // ...
    15     }
    16     public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
    17     // ...
    18     }
    19     public Mono<ServerResponse> deleteUser(ServerRequest request) {
    20     // ...
    21     }
    22 }
    

    如果你同时添加了spring-boot-starter-web和spring-boot-starter-webflux依赖,那么Spring Boot会自动配置Spring MVC,而不是WebFlux。你当然可以强制指定应用类型,通过SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 

    3. Hello WebFlux

    pom.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <parent>
     6         <groupId>org.springframework.boot</groupId>
     7         <artifactId>spring-boot-starter-parent</artifactId>
     8         <version>2.2.5.RELEASE</version>
     9         <relativePath/> <!-- lookup parent from repository -->
    10     </parent>
    11     <groupId>com.cjs.example</groupId>
    12     <artifactId>cjs-reactive-rest-service</artifactId>
    13     <version>0.0.1-SNAPSHOT</version>
    14     <name>cjs-reactive-rest-service</name>
    15 
    16     <properties>
    17         <java.version>1.8</java.version>
    18     </properties>
    19 
    20     <dependencies>
    21         <dependency>
    22             <groupId>org.springframework.boot</groupId>
    23             <artifactId>spring-boot-starter-webflux</artifactId>
    24         </dependency>
    25 
    26         <dependency>
    27             <groupId>org.springframework.boot</groupId>
    28             <artifactId>spring-boot-starter-test</artifactId>
    29             <scope>test</scope>
    30             <exclusions>
    31                 <exclusion>
    32                     <groupId>org.junit.vintage</groupId>
    33                     <artifactId>junit-vintage-engine</artifactId>
    34                 </exclusion>
    35             </exclusions>
    36         </dependency>
    37         <dependency>
    38             <groupId>io.projectreactor</groupId>
    39             <artifactId>reactor-test</artifactId>
    40             <scope>test</scope>
    41         </dependency>
    42     </dependencies>
    43 
    44     <build>
    45         <plugins>
    46             <plugin>
    47                 <groupId>org.springframework.boot</groupId>
    48                 <artifactId>spring-boot-maven-plugin</artifactId>
    49             </plugin>
    50         </plugins>
    51     </build>
    52 
    53 </project>
    

    GreetingHandler.java 

     1 package com.cjs.example.restservice.hello;
     2 
     3 import org.springframework.http.MediaType;
     4 import org.springframework.stereotype.Component;
     5 import org.springframework.web.reactive.function.BodyInserters;
     6 import org.springframework.web.reactive.function.server.ServerRequest;
     7 import org.springframework.web.reactive.function.server.ServerResponse;
     8 import reactor.core.publisher.Mono;
     9 
    10 import java.util.concurrent.atomic.AtomicLong;
    11 
    12 /**
    13  * @author ChengJianSheng
    14  * @date 2020-03-25
    15  */
    16 @Component
    17 public class GreetingHandler {
    18 
    19     private final AtomicLong counter = new AtomicLong();
    20 
    21     /**
    22      * A handler to handle the request and create a response
    23      */
    24     public Mono<ServerResponse> hello(ServerRequest request) {
    25         return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
    26                 .body(BodyInserters.fromValue("Hello, Spring!"));
    27 
    28     }
    29 }
    

    GreetingRouter.java

     1 package com.cjs.example.restservice.hello;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 import org.springframework.context.annotation.Configuration;
     5 import org.springframework.http.MediaType;
     6 import org.springframework.web.reactive.function.server.*;
     7 
     8 /**
     9  * @author ChengJianSheng
    10  * @date 2020-03-25
    11  */
    12 @Configuration
    13 public class GreetingRouter {
    14 
    15     /**
    16      * The router listens for traffic on the /hello path and returns the value provided by our reactive handler class.
    17      */
    18     @Bean
    19     public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
    20         return RouterFunctions.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
    21     }
    22 }
    

    GreetingWebClient.java

     1 package com.cjs.example.restservice.hello;
     2 
     3 import org.springframework.http.MediaType;
     4 import org.springframework.web.reactive.function.client.ClientResponse;
     5 import org.springframework.web.reactive.function.client.WebClient;
     6 import reactor.core.publisher.Mono;
     7 
     8 /**
     9  * @author ChengJianSheng
    10  * @date 2020-03-25
    11  */
    12 public class GreetingWebClient {
    13 
    14     /**
    15      * For reactive applications, Spring offers the WebClient class, which is non-blocking.
    16      *
    17      * WebClient can be used to communicate with non-reactive, blocking services, too.
    18      */
    19     private WebClient client = WebClient.create("http://localhost:8080");
    20 
    21     private Mono<ClientResponse> result = client.get()
    22             .uri("/hello")
    23             .accept(MediaType.TEXT_PLAIN)
    24             .exchange();
    25 
    26     public String getResult() {
    27         return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
    28     }
    29 }
    

    Application.java

     1 package com.cjs.example.restservice;
     2 
     3 import com.cjs.example.restservice.hello.GreetingWebClient;
     4 import org.springframework.boot.SpringApplication;
     5 import org.springframework.boot.autoconfigure.SpringBootApplication;
     6 
     7 /**
     8  * @author ChengJianSheng
     9  * @date 2020-03-25
    10  */
    11 @SpringBootApplication
    12 public class CjsReactiveRestServiceApplication {
    13 
    14     public static void main(String[] args) {
    15         SpringApplication.run(CjsReactiveRestServiceApplication.class, args);
    16 
    17         GreetingWebClient gwc = new GreetingWebClient();
    18         System.out.println(gwc.getResult());
    19     }
    20 
    21 } 

    可以直接在浏览器中访问 http://localhost:8080/hello  

    GreetingRouterTest.java

     1 package com.cjs.example.restservice;
     2 
     3 import org.junit.jupiter.api.Test;
     4 import org.junit.jupiter.api.extension.ExtendWith;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.boot.test.context.SpringBootTest;
     7 import org.springframework.http.MediaType;
     8 import org.springframework.test.context.junit.jupiter.SpringExtension;
     9 import org.springframework.test.web.reactive.server.WebTestClient;
    10 
    11 @ExtendWith(SpringExtension.class)
    12 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    13 public class GreetingRouterTest {
    14 
    15     @Autowired
    16     private WebTestClient webTestClient;
    17 
    18     /**
    19      * Create a GET request to test an endpoint
    20      */
    21     @Test
    22     public void testHello() {
    23         webTestClient.get()
    24                 .uri("/hello")
    25                 .accept(MediaType.TEXT_PLAIN)
    26                 .exchange()
    27                 .expectStatus().isOk()
    28                 .expectBody(String.class).isEqualTo("Hello, Spring!");
    29     }
    30 
    31 }

    4. Reactor 核心特性

    Mono: implements Publisher and returns 0 or 1 elements

    Flux: implements Publisher and returns N elements

     

     

     

    5. Spring Data Redis

    pom.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <parent>
     6         <groupId>org.springframework.boot</groupId>
     7         <artifactId>spring-boot-starter-parent</artifactId>
     8         <version>2.2.6.RELEASE</version>
     9         <relativePath/> <!-- lookup parent from repository -->
    10     </parent>
    11     <groupId>com.cjs.example</groupId>
    12     <artifactId>cjs-webflux-hello</artifactId>
    13     <version>0.0.1-SNAPSHOT</version>
    14     <name>cjs-webflux-hello</name>
    15 
    16     <properties>
    17         <java.version>1.8</java.version>
    18     </properties>
    19 
    20     <dependencies>
    21         <dependency>
    22             <groupId>org.springframework.boot</groupId>
    23             <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    24         </dependency>
    25         <dependency>
    26             <groupId>org.springframework.boot</groupId>
    27             <artifactId>spring-boot-starter-webflux</artifactId>
    28         </dependency>
    29 
    30         <dependency>
    31             <groupId>org.apache.commons</groupId>
    32             <artifactId>commons-pool2</artifactId>
    33             <version>2.8.0</version>
    34         </dependency>
    35         <dependency>
    36             <groupId>com.alibaba</groupId>
    37             <artifactId>fastjson</artifactId>
    38             <version>1.2.67</version>
    39         </dependency>
    40         <dependency>
    41             <groupId>org.projectlombok</groupId>
    42             <artifactId>lombok</artifactId>
    43             <optional>true</optional>
    44         </dependency>
    45         <dependency>
    46             <groupId>org.springframework.boot</groupId>
    47             <artifactId>spring-boot-starter-test</artifactId>
    48             <scope>test</scope>
    49             <exclusions>
    50                 <exclusion>
    51                     <groupId>org.junit.vintage</groupId>
    52                     <artifactId>junit-vintage-engine</artifactId>
    53                 </exclusion>
    54             </exclusions>
    55         </dependency>
    56         <dependency>
    57             <groupId>io.projectreactor</groupId>
    58             <artifactId>reactor-test</artifactId>
    59             <scope>test</scope>
    60         </dependency>
    61     </dependencies>
    62 
    63     <build>
    64         <plugins>
    65             <plugin>
    66                 <groupId>org.springframework.boot</groupId>
    67                 <artifactId>spring-boot-maven-plugin</artifactId>
    68             </plugin>
    69         </plugins>
    70     </build>
    71 
    72 </project>
    

    UserController.java

     1 package com.cjs.example.webflux.controller;
     2 
     3 import com.alibaba.fastjson.JSON;
     4 import com.cjs.example.webflux.domain.User;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.data.redis.core.ReactiveHashOperations;
     7 import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
     8 import org.springframework.web.bind.annotation.*;
     9 import reactor.core.publisher.Mono;
    10 
    11 /**
    12  * @author ChengJianSheng
    13  * @date 2020-03-27
    14  */
    15 @RestController
    16 @RequestMapping("/users")
    17 public class UserController {
    18 
    19 
    20     @Autowired
    21     private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
    22 
    23     @GetMapping("/hello")
    24     public Mono<String> hello() {
    25         return Mono.just("Hello, Reactive");
    26     }
    27 
    28     @PostMapping("/save")
    29     public Mono<Boolean> saveUser(@RequestBody User user) {
    30         ReactiveHashOperations hashOperations = reactiveStringRedisTemplate.opsForHash();
    31         return hashOperations.put("USER_HS", String.valueOf(user.getId()), JSON.toJSONString(user));
    32     }
    33 
    34     @GetMapping("/info/{id}")
    35     public Mono<User> info(@PathVariable Integer id) {
    36         ReactiveHashOperations reactiveHashOperations = reactiveStringRedisTemplate.opsForHash();
    37         Mono<String> hval = reactiveHashOperations.get("USER_HS", String.valueOf(id));
    38         return hval.map(e->JSON.parseObject(e, User.class));
    39     }
    40 
    41 }
    

    CoffeeController.java

     1 package com.cjs.example.webflux.controller;
     2 
     3 import com.cjs.example.webflux.domain.Coffee;
     4 import org.springframework.data.redis.core.*;
     5 import org.springframework.web.bind.annotation.GetMapping;
     6 import org.springframework.web.bind.annotation.PathVariable;
     7 import org.springframework.web.bind.annotation.RequestMapping;
     8 import org.springframework.web.bind.annotation.RestController;
     9 import reactor.core.publisher.Flux;
    10 import reactor.core.publisher.Mono;
    11 
    12 /**
    13  * Spring WebFlux is the new reactive web framework introduced in Spring Framework 5.0.
    14  * Unlike Spring MVC, it does not require the Servlet API, is fully asynchronous and non-blocking,
    15  * and implements the Reactive Streams specification through the Reactor project.
    16  *
    17  * @author ChengJianSheng
    18  * @date 2020-03-27
    19  */
    20 @RestController
    21 @RequestMapping("/coffees")
    22 public class CoffeeController {
    23 
    24     private final ReactiveRedisOperations<String, Coffee> coffeeOps;
    25 
    26     public CoffeeController(ReactiveRedisOperations<String, Coffee> coffeeOps) {
    27         this.coffeeOps = coffeeOps;
    28     }
    29 
    30     @GetMapping("/getAll")
    31     public Flux<Coffee> getAll() {
    32         return coffeeOps.keys("*").flatMap(coffeeOps.opsForValue()::get);
    33     }
    34 
    35     @GetMapping("/info/{id}")
    36     public Mono<Coffee> info(@PathVariable String id) {
    37         ReactiveValueOperations valueOperations = coffeeOps.opsForValue();
    38         return valueOperations.get(id);
    39     }
    40 } 

    最后,也是非常重要的一点:异步非阻塞并不会使程序运行得更快。WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。

    Spring WebFlux 是一个异步非阻塞的 Web 框架,所以,它特别适合应用在 IO 密集型的服务中,比如微服务网关这样的应用中。

    Reactive and non-blocking generally do not make applications run faster.

    6. Docs

    https://spring.io/ 

    https://spring.io/reactive 

    https://projectreactor.io/docs/core/release/reference/index.html

    https://projectreactor.io/docs/core/release/reference/index.html#core-features 

    https://docs.spring.io/spring/docs/

    https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/index.html

    https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web-reactive.html#webflux

    https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web-reactive.html#webflux-reactive-spring-web

    https://www.cnblogs.com/diegodu/p/8794857.html

  • 相关阅读:
    3变量
    2python的第一个程序
    1python语言的基本介绍
    6文字的剪切,复制,移动
    讨厌的Permission denied:adb访问手机目录时,怎么处理Permission denied问题
    adb命令整理(持续整理)
    loadrunner12下载、安装、认证、汉化
    操作系统中堆和栈的区别
    mos管选型
    Office 转 PDF
  • 原文地址:https://www.cnblogs.com/cjsblog/p/12580518.html
Copyright © 2020-2023  润新知