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】命令式的、同步阻塞的

Spring WebFlux 入门_第1张图片

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 
 2  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     4.0.0
 5     
 6         org.springframework.boot
 7         spring-boot-starter-parent
 8         2.2.5.RELEASE
 9          
10     
11     com.cjs.example
12     cjs-reactive-rest-service
13     0.0.1-SNAPSHOT
14     cjs-reactive-rest-service
15 
16     
17         1.8
18     
19 
20     
21         
22             org.springframework.boot
23             spring-boot-starter-webflux
24         
25 
26         
27             org.springframework.boot
28             spring-boot-starter-test
29             test
30             
31                 
32                     org.junit.vintage
33                     junit-vintage-engine
34                 
35             
36         
37         
38             io.projectreactor
39             reactor-test
40             test
41         
42     
43 
44     
45         
46             
47                 org.springframework.boot
48                 spring-boot-maven-plugin
49             
50         
51     
52 
53 

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

Spring WebFlux 入门_第2张图片 

Spring WebFlux 入门_第3张图片 

Spring WebFlux 入门_第4张图片 

5. Spring Data Redis

pom.xml

 1 
 2  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     4.0.0
 5     
 6         org.springframework.boot
 7         spring-boot-starter-parent
 8         2.2.6.RELEASE
 9          
10     
11     com.cjs.example
12     cjs-webflux-hello
13     0.0.1-SNAPSHOT
14     cjs-webflux-hello
15 
16     
17         1.8
18     
19 
20     
21         
22             org.springframework.boot
23             spring-boot-starter-data-redis-reactive
24         
25         
26             org.springframework.boot
27             spring-boot-starter-webflux
28         
29 
30         
31             org.apache.commons
32             commons-pool2
33             2.8.0
34         
35         
36             com.alibaba
37             fastjson
38             1.2.67
39         
40         
41             org.projectlombok
42             lombok
43             true
44         
45         
46             org.springframework.boot
47             spring-boot-starter-test
48             test
49             
50                 
51                     org.junit.vintage
52                     junit-vintage-engine
53                 
54             
55         
56         
57             io.projectreactor
58             reactor-test
59             test
60         
61     
62 
63     
64         
65             
66                 org.springframework.boot
67                 spring-boot-maven-plugin
68             
69         
70     
71 
72 

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

你可能感兴趣的:(Spring WebFlux 入门)