zoukankan      html  css  js  c++  java
  • SpringBoot 2.x (14):WebFlux响应式编程

    响应式编程生活案例:

    传统形式:

    一群人去餐厅吃饭,顾客1找服务员点餐,服务员把订单交给后台厨师,然后服务员等待,

    当后台厨师做好饭,交给服务员,经过服务员再交给顾客1,依此类推,该服务员再招待顾客2。

    服务员可以理解为服务器,服务器越多,可处理的顾客请求越多

    响应式编程:

    服务员记住到顾客1的要求,交给后台厨师,再记住顾客2的要求,交给后台厨师,依此类推

    当厨师做好顾客1的饭,告知服务员,然后服务员把饭送到顾客1;

    当厨师做好顾客2的饭,告知服务员,然后服务员把饭送到顾客2,依此类推

    一系列的事件称为流,异步非阻塞,观察者的设计模式

    代码案例:

    传统:

    int b=2;
    int c=3
    int a=b+c //a被赋值后,b和c的改变不会影响a
    b=5;

    响应式编程:

    int b=2;
    int c=3
    int a=b+c 
    b=5;//此时a变化为8,a会根据b、c的变化而变化

    SpringBoot2.x的响应式编程基于Spring5;

    而Spring5的响应式编程又基于Reactor和Netty、Spring WebFlux替代Spring MVC

    响应式编程最大的核心是非阻塞,即后台的每一步每一段都要做到非阻塞

    比如使用MySQL作为数据库,由于MySQL不提供响应式编程,所以会阻塞

    因此响应式编程不应采用MySQL,应该使用非阻塞的NoSQL

    Spring WebFlux有两种风格:基于功能和基于注解的。基于注解非常接近Spring MVC模型,如以下示例所示:

                @RestController 
                @RequestMapping(“/ users”)
                 public  class MyRestController {
    
                    @GetMapping(“/ {user}”)
                     public Mono <User> getUser( @PathVariable Long user){
                         // ...
                    }
    
                    @GetMapping(“/ {user} / customers”)
                     public Flux <Customer> getUserCustomers( @PathVariable Long user){
                         // ...
                    }
    
                    @DeleteMapping(“/ {user}”)
                     public Mono <User> deleteUser( @PathVariable Long user){
                         // ...
                    }
    
                }

    第二种: 路由配置与请求的实际处理分开

                @Configuration
                 public  class RoutingConfiguration {
    
                    @Bean
                     public RouterFunction <ServerResponse> monoRouterFunction(UserHandler userHandler){
                         return route(GET( “/ {user}”).and(accept(APPLICATION_JSON)),userHandler :: getUser)
                                .andRoute(GET(“/ {user} / customers”).and(accept(APPLICATION_JSON)),userHandler :: getUserCustomers)
                                .andRoute(DELETE(“/ {user}”).and(accept(APPLICATION_JSON)),userHandler :: deleteUser);
                    }
    
                }
    
                @Component
                public class UserHandler {
    
                    public Mono <ServerResponse> getUser(ServerRequest request){
                         // ...
                    }
    
                    public Mono <ServerResponse> getUserCustomers(ServerRequest request){
                         // ...
                    }
    
                    public Mono <ServerResponse> deleteUser(ServerRequest request){
                         // ...
                    }
                }

    Spring WebFlux应用程序不严格依赖于Servlet API,因此它们不能作为war文件部署,也不能使用src/main/webapp目录

    可以整合多个模板引擎,除了REST外,您还可以使用Spring WebFlux提供动态HTML内容

    Spring WebFlux支持各种模板技术,包括Thymeleaf,FreeMarker

    简单的实战:

    依赖

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

    自动生成的SpringBoot项目还会有一个test依赖,可选

            <dependency>
                <groupId>io.projectreactor</groupId>
                <artifactId>reactor-test</artifactId>
                <scope>test</scope>
            </dependency>

    简单的Controller:

    package org.dreamtech.webflux.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import reactor.core.publisher.Mono;
    
    @RestController
    public class TestController {
        @GetMapping("/test")
        public Mono<String> test() {
            return Mono.just("hello world");
        }
    }

    访问http://localhost:8080/test,显示hello world说明成功

    这里使用到了Mono,后边还会用到Flux,他们的实现很复杂,但可以简单地理解:

    User、List<User>

    1)简单业务而言:和其他普通对象差别不大,复杂请求业务,就可以提升性能
    2)通俗理解:
    Mono 表示的是包含 0 或者 1 个元素的异步序列
    mono->单一对象 User
    例如从redis根据用户ID查到唯一的用户,然后进行返回Mono<User>

    Flux 表示的是包含 0 到 N 个元素的异步序列
    flux->数组列表对象 List<User>
    例如从redis根据条件:性别为男性的用户进行查找,然后返回Flux<User>
    3)Flux 和 Mono 之间可以进行转换

    进一步的使用

    对User实体类实现增删改查功能:

    package org.dreamtech.webflux.domain;
    
    public class User {
        private String id;
        private String name;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public User(String id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    
    }

    Service:

    package org.dreamtech.webflux.service;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.dreamtech.webflux.domain.User;
    import org.springframework.stereotype.Service;
    
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    @Service
    public class UserService {
        // 使用Map模拟数据库
        private static final Map<String, User> dataMap = new HashMap<String, User>();
        static {
            dataMap.put("1", new User("1", "admin"));
            dataMap.put("2", new User("2", "John"));
            dataMap.put("3", new User("3", "Rose"));
            dataMap.put("4", new User("4", "James"));
            dataMap.put("5", new User("5", "Bryant"));
        }
    
        /**
         * 返回数据库的所有用户信息
         * 
         * @return
         */
        public Flux<User> list() {
            Collection<User> list = UserService.dataMap.values();
            return Flux.fromIterable(list);
        }
    
        /**
         * 根据用户ID返回用户信息
         * 
         * @param id 用户ID
         * @return
         */
        public Mono<User> getById(final String id) {
            return Mono.justOrEmpty(UserService.dataMap.get(id));
        }
    
        /**
         * 根据用户ID删除用户
         * 
         * @param id 用户ID
         * @return
         */
        public Mono<User> delete(final String id) {
            return Mono.justOrEmpty(UserService.dataMap.remove(id));
        }
    }

    Controller:

    package org.dreamtech.webflux.controller;
    
    import org.dreamtech.webflux.domain.User;
    import org.dreamtech.webflux.service.UserService;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    @RestController
    public class UserController {
        private final UserService userService;
    
        public UserController(final UserService userService) {
            this.userService = userService;
        }
    
        /**
         * 根据ID查找用户
         * 
         * @param id 用户ID
         * @return
         */
        @GetMapping("/find")
        public Mono<User> findById(final String id) {
            return userService.getById(id);
        }
    
        /**
         * 获得用户列表
         * 
         * @return
         */
        @GetMapping("/list")
        public Flux<User> list() {
            return userService.list();
        }
    
        /**
         * 根据ID删除用户
         * 
         * @param id 用户ID
         * @return
         */
        @GetMapping("/delete")
        public Mono<User> delete(final String id) {
            return userService.delete(id);
        }
    }

    访问定义的三个API,发现和SpringMVC基本没有区别

    所以,对返回进行延迟处理:

        @GetMapping("/list")
        public Flux<User> list() {
            return userService.list().delayElements(Duration.ofSeconds(3));
        }

    只是这些设置的话,等待3*list.size秒后全部返回,要突出流的特点,需要进行配置:

        @GetMapping(value = "/list", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
        public Flux<User> list() {
            return userService.list().delayElements(Duration.ofSeconds(3));
        }

    这时候访问,可以发现每过3秒返回一个对象信息

    使用WebClient客户端进行测试:

    package org.dreamtech.webflux;
    
    import org.junit.Test;
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.client.WebClient;
    
    import reactor.core.publisher.Mono;
    
    public class WebClientTest {
        @Test
        public void test() {
            Mono<String> bodyMono = WebClient.create().get().uri("http://localhost:8080/find?id=3")
                    .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class);
            System.out.println(bodyMono.block());
        }
    }
  • 相关阅读:
    POJ 2723 Get Luffy Out(2-SAT)
    ZOJ 3613 Wormhole Transport
    HDU 4085 Peach Blossom Spring
    NBUT 1221 Intermediary
    NBUT 1223 Friends number
    NBUT 1220 SPY
    NBUT 1218 You are my brother
    PAT 1131. Subway Map (30)
    ZSTU OJ 4273 玩具
    ZSTU OJ 4272 最佳淘汰算法
  • 原文地址:https://www.cnblogs.com/xuyiqing/p/10855508.html
Copyright © 2011-2022 走看看