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());
    }
}

原文地址:https://www.cnblogs.com/xuyiqing/p/10855508.html

时间: 2024-10-07 16:54:34

SpringBoot 2.x (14):WebFlux响应式编程的相关文章

SpringBoot使用WebFlux响应式编程操作数据库

这一篇文章介绍SpringBoot使用WebFlux响应式编程操作MongoDb数据库. 前言 在之前一篇简单介绍了WebFlux响应式编程的操作,我们在来看一下下图,可以看到,在目前的Spring WebFlux还没有支持类似Mysql这样的关系型数据库,所以本文以MongoDb数据库为例. SpringBoot使用WebFlux响应式编程操作数据库 接下来介绍SpringBoot使用WebFlux响应式编程操作MongoDb数据库. 新建项目 pom文件 新建项目,在项目中加入webflux

[转]springboot2 webflux 响应式编程学习路径

原文链接 spring官方文档 springboot2 已经发布,其中最亮眼的非webflux响应式编程莫属了!响应式的weblfux可以支持高吞吐量,意味着使用相同的资源可以处理更加多的请求,毫无疑问将会成为未来技术的趋势,是必学的技术!很多人都看过相关的入门教程,但看完之后总觉得很迷糊,知其然不知道其所以然,包括我本人也有相同的疑惑.后面在研究和学习中发现,是我的学习路径不对,很多基本概念不熟悉,之前公司主打的jdk版本还是1.6/1.7,直接跳到运行在jdk8上的webflux,跨度太大,

SpringBoot2.0不容错过的新特性 WebFlux响应式编程

第1章 课程介绍 课程介绍及导学 第2章 函数式编程和lambda表达式 本章介绍函数式编程的概念,和lambda表达式的基础语法,并分析了惰性求值的应用和实现.最后同意反编译字节码,重点剖析了lambda表达式的底层实现原理 第3章 Stream流编程 本章介绍jdk8里面stream流编程的重要知识点,并剖析流的运行机制和实现原理 第4章 reactive stream 响应式流 本章介绍jdk9的响应式流的开发过程,重点讲解响应式流的4个接口,以及背压的概念和jdk实现背压的关键. 第5章

Java响应式编程Springboot WebFlux基础与实战

在这里我个人推荐的学习途径如下:先学习jdk8的lambda表达式和stream流编程,了解函数式编程的知识点和思想,接着学习jdk9的响应式流flux,理解响应式流概念,理解背压和实现机制.这2者学好之后,很容易理解webflux的基石reactor,再学习webflux就水到渠成了! 这里我记录了自己的学习之路,列出了每一块的学习重点,除了API的知识点学习之外,更加重要的了解底层运行机制和实现原理.对于我个人来说,学习技术如果不了解原理,知识点需要死记硬背,而了解了底层机制之后,不但不需要

Spring Boot (十四): 响应式编程以及 Spring Boot Webflux 快速入门

1. 什么是响应式编程 在计算机中,响应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和变化传播的编程范式.这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播. 例如,在命令式编程环境中,a=b+c 表示将表达式的结果赋给 a,而之后改变 b 或 c 的值不会影响 a .但在响应式编程中,a 的值会随着 b 或 c 的更新而更新. 响应式编程是基于异步和事件驱动的非阻塞程序,只需要在程序内启动少量线程扩

springboot 使用webflux响应式开发教程(二)

本篇是对springboot 使用webflux响应式开发教程(一)的进一步学习. 分三个部分: 数据库操作webservicewebsocket 创建项目,artifactId = trading-service,groupId=io.spring.workshop.选择Reactive Web , Devtools, Thymeleaf , Reactive Mongo.WEB容器spring-boot-starter-webflux 附带了 spring-boot-starter-reac

Spring5.0响应式编程入门

引言? 响应式编程是一种面向数据流和变化传播的编程范式.使用它可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播.我们可以使用声明的方式构建应用程序的能力,形成更加敏感和有弹性的应用,所以Spring 5在其核心框架中增加了反应系统,已经开始向声明式编程的范式转变. 响应式编程的优势 提高了代码的可读性,因此开发人员只需要关注定义业务逻辑. 在高并发环境中,可以自然的处理消息. 可以控制生产者和消费者之间的流量,避免内存不足. 对于一个或多个线程,

使用ReactiveCocoa实现iOS平台响应式编程

使用ReactiveCocoa实现iOS平台响应式编程 ReactiveCocoa和响应式编程 在说ReactiveCocoa之前,先要介绍一下FRP(Functional Reactive Programming,响应式编程),在维基百科中有这样一个例子介绍: 在命令式编程环境中,a = b + c 表示将表达式的结果赋给a,而之后改变b或c的值不会影响a.但在响应式编程中,a的值会随着b或c的更新而更新. Excel就是响应式编程的一个例子.单元格可以包含字面值或类似"=B1+C1″的公式,

高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介

原创文章,转载请声明出处哈. ReactiveCocoa函数响应式编程 一.简介 ReactiveCocoa(其简称为RAC)是函数响应式编程框架.RAC具有函数式编程和响应式编程的特性.它主要吸取了.Net的 Reactive Extensions的设计和实现. 函数式编程 (Functional Programming) 函数式编程也可以写N篇,它是完全不同于OO的编程模式,这里主要讲一下这个框架使用到的函数式思想. 1) 高阶函数:在函数式编程中,把函数当参数来回传递,而这个,说成术语,我