Spring WebFlux 教程:如何构建反应式 Web 应用程序

反应式系统提供了我们在高数据流世界中所需的无与伦比的响应能力和可扩展性。然而,反应式系统需要经过专门培训的工具和开发人员来实现这些独特的程序架构。Spring WebFlux with Project Reactor 是一个专门为满足现代公司的响应式需求而构建的框架。

今天,我们将通过解释 WebFlux 如何与其他反应式堆栈工具配合、有何不同以及如何制作您的第一个应用程序来帮助您开始使用 WebFlux。

什么是反应式系统?

反应式系统是采用反应式架构模式设计的系统,该模式优先使用松耦合、灵活和可扩展的组件。它们的设计还考虑了故障解决方案,以确保即使出现故障,大部分系统仍能运行。

反应式系统专注于:

  • 反应性:最重要的是,反应性系统应该对任何用户输入做出快速响应。反应式系统倡导者认为,反应式有助于优化系统的所有其他部分,从数据收集到用户体验。

  • 弹性:反应式系统的设计应该能够预测系统故障。反应式系统期望组件最终会失效,并设计松散耦合的系统,即使几个单独的部件停止工作也能保持活动状态。

  • 弹性:反应式系统应该通过扩大或缩小以满足需求来适应工作负载的大小。许多反应式系统还将使用预测性扩展来预测和准备突然变化。实现弹性的关键是消除任何瓶颈并构建可以根据需要分片或复制组件的系统。

  • 消息驱动的通信:反应式系统的所有组件都是松散耦合的,每个组件之间都有硬边界。您的系统应该通过显式消息传递跨越这些边界进行通信。这些消息让不同的组件了解故障,并帮助他们将工作流委派给可以处理它的组件。

反应式和其他 Web 模式之间最显着的区别是反应式系统可以一次执行多个未阻塞的调用,而不是让一些调用等待其他调用。因此,响应式系统可以提高性能和响应速度,因为 Web 应用程序的每个部分都可以比必须等待另一部分更快地完成自己的部分。

什么是反应堆项目?

Project Reactor 是一个由 Pivotal 构建并由 Spring 提供支持的框架。它实现了反应式 API 模式,最著名的是反应式流规范。

如果您熟悉Java 8 Streams,您会很快发现 Stream 和 Flux(或其单元素版本 Mono)之间的许多相似之处。它们之间的主要区别在于 Fluxes 和 Monos 遵循一种publisher-subscriber模式并实现背压,而 Stream API 则没有。

背压是数据端点向数据生产者发出信号,表明它接收了太多数据的一种方式。这允许更好的流量管理和分配,因为它可以防止单个组件过度工作。

使用 Reactor 的主要优点是您可以完全控制数据流。您可以依靠订阅者在准备好处理信息时询问更多信息的能力,或者在发布者端缓冲一些结果,甚至使用没有背压的全推送方法。

在我们的反应式堆栈中,它位于 Spring Boot 2.0 和 WebFlux 之上:

示例反应式堆栈

堆栈:技术堆栈是用于创建 Web 或移动应用程序的软件产品和编程语言的组合。反应式堆栈是相同的,但用于创建反应式应用程序。

什么是 Spring WebFlux?

Spring WebFlux 是一个完全非阻塞、基于注解的 Web 框架,它构建在 Project Reactor 之上,它使得在 HTTP 层上构建响应式应用程序成为可能。WebFlux 使用新的路由器功能特性将函数式编程应用于 Web 层并绕过声明性控制器和请求映射。WebFlux 要求您将 Reactor 作为核心依赖项导入。

WebFlux 作为Spring MVC的响应式替代品在 Spring 5 中添加,并增加了对以下内容的支持:

  • 非阻塞线程:无需等待先前任务完成即可完成指定任务的并发线程。
  • Reactive Stream API:一种标准化工具,包括用于非阻塞背压的异步流处理选项。
  • 异步数据处理:当数据在后台处理并且用户可以不间断地继续使用正常的应用程序功能时。

最终WebFlux摒弃了SpringMVC的多请求线程模型,而是使用多EventLoop非阻塞模型来启用反应式、可扩展的应用程序。由于支持Netty、Undertow 和Servlet 3.1+ 容器等流行服务器,WebFlux 已成为反应式堆栈的关键部分。

Router功能

RouterFunction是标准springmvc中使用的@RequestMapping和@Controller注释样式的一种功能替代。

我们可以使用它将请求路由到处理程序函数:

  • 传统的路由定义
@RestController
public class ProductController {
    @RequestMapping("/product")
    public List<Product> productListing() {
        return ps.findAll();
    }
}
  • 函数式定义
@Bean
public RouterFunction<ServerResponse> productListing(ProductService ps) {
    return route().GET("/product", req -> ok().body(ps.findAll()))
      .build();
}

你可以使用RouterFunctions.route()来创建路由,而不是编写完整的路由器函数。路由注册为spring的bean,因此可以在任何配置类中创建。 路由器功能避免了由请求映射的多步骤过程引起的潜在副作用,而是将其简化为直接的路由器/处理程序链。这允许函数式编程实现反应式编程。

RequestMapping和Controller注释样式在WebFlux中仍然有效如果您对旧样式更熟悉,RouterFunctions只是解决方案的一个新选项。

WebClient 详解

项目中经常用到发送Http请求的客户端,如果你使用webflux那非常简单去创建一个Http请求。WebClient是WebFlux的反应式web客户端,它是从著名的rest模板构建的。它是一个接口,表示web请求的主要入口点,并支持同步和异步操作。WebClient主要用于反应式后端到后端通信。

您可以通过使用Maven导入标准WebFlux依赖项来构建和创建WebClient实例:

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

创建实例


WebClient webClient = WebClient.create();
// 如果是调用特定服务的API,可以在初始化webclient 时使用,baseUrl
WebClient webClient = WebClient.create("https://github.com/1ssqq1lxr");

或者构造器方式初始化

WebClient webClient1 = WebClient.builder()
    .baseUrl("https://github.com/1ssqq1lxr")
    .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json")
    .defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient")
    .build();
  • Get请求
Mono<String> resp = WebClient.create()
      .method(HttpMethod.GET)
      .uri("https://github.com/1ssqq1lxr")
      .cookie("token","xxxx")
      .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
      .retrieve().bodyToMono(String.class);
  • Post请求(表单)
 MultiValueMap<String, String> formData = new LinkedMultiValueMap();
 formData.add("name1","value1");
 formData.add("name2","value2");
 Mono<String> resp = WebClient.create().post()
       .uri("http://www.w3school.com.cn/test/demo_form.asp")
       .contentType(MediaType.APPLICATION_FORM_URLENCODED)
       .body(BodyInserters.fromFormData(formData))
       .retrieve().bodyToMono(String.class);
  • Post请求(Body)
Book book = new Book();
book.setName("name");
book.setTitle("this is title");
Mono<String> resp = WebClient.create().post()
        .uri("https://github.com/1ssqq1lxr")
        .contentType(MediaType.APPLICATION_JSON_UTF8)
        .body(Mono.just(book),Book.class)
        .retrieve().bodyToMono(String.class);
  • 文件上传
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
HttpEntity<ClassPathResource> entity = new HttpEntity<>(new ClassPathResource("parallel.png"), headers);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
arts.add("file", entity);
Mono<String> resp = WebClient.create().post()
        .uri("http://localhost:8080/upload")
        .contentType(MediaType.MULTIPART_FORM_DATA)
        .body(BodyInserters.fromMultipartData(parts))
        .retrieve().bodyToMono(String.class);

Reactive Steam API

下篇文章给大家详细讲下Reactor3的API

Reactive Stream API是一个的函数集合,允许更智能的流数据流。它内置了对背压和异步处理的支持,确保应用程序最有效地利用计算机和组件资源。

反应流API有四个主要接口:

  • Publisher:根据链接订阅者的需求向他们发布事件。充当订户可以监视事件的中心链接点。

  • Subscriber:接收和处理发布服务器发出的事件。多个订阅服务器可以链接到单个发布服务器,并对同一事件做出不同的响应。订户可以设置为反应:

    • onNext,当它接收到下一个事件时。

    • onSubscribe,添加新订阅时

    • onError,当另一个订阅服务器发生错误时

    • onComplete,当一个订阅完成时

Server容器

WebFlux在Tomcat、Jetty、servlet3.1+容器以及Netty和Undertow等非Servlet运行时上都受支持。Netty最常用于异步和非阻塞设计,因此WebFlux将默认使用它。只需对Maven或Gradle构建软件进行简单的更改,就可以轻松地在这些服务器选项之间切换。

这使得WebFlux在它可以使用的技术方面具有高度的通用性,并允许您使用现有的基础设施轻松地实现它。

并发模型

WebFlux是以无阻塞的思想构建的,因此使用了与springmvc不同的并发编程模型。

springmvc假设线程将被阻塞,并在阻塞实例期间使用一个大的线程池来保持移动。这个更大的线程池使得MVC资源更密集,因为计算机硬件必须同时保持更多的线

WebFlux使用了一个小的线程池,因为它假设您永远不需要通过工作来避免阻塞。这些线程称为事件循环工作线程,数量固定,在传入请求中的循环速度比MVC线程快。这意味着WebFlux更有效地使用计算机资源,因为活动线程总是在工作。

Spring WebFlux Security

WebFlux使用Spring安全性来实现身份验证和授权协议。springsecurity使用WebFilter根据经过身份验证的用户列表认证请求。

@EnableWebFluxSecurity
public class HelloWebFluxSecurityConfig {
    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("user")
            .roles("USER")
            .build();
        return new MapReactiveUserDetailsService(user);
    }
}

在这里,我们可以看到用户有一个用户名、一个密码和一个或多个roles标签,这些标签允许自定义定访问。类似于SpringBoot Security的 UserDetailsService接口

开始使用 Spring WebFlux

生成项目

spring代码生成器

参考配置

生成后的pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.5.1</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.github.webflux.learn</groupId>
 <artifactId>demo</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>demo</name>
 <description>Demo project for Spring Boot</description>
 <properties>
  <java.version>1.8</java.version>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>

  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>io.projectreactor</groupId>
   <artifactId>reactor-test</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
     <excludes>
      <exclude>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
      </exclude>
     </excludes>
    </configuration>
   </plugin>
  </plugins>
 </build>

</project>

开发接口

自定义一个函数路由:将请求path中的占位参数获取作为返回值

/**
 * @author coding途中
 */
@Configuration
public class TestRouter {

    @Bean
    public RouterFunction<ServerResponse> routeExample() {
        return RouterFunctions
                .route(RequestPredicates.GET("/hello/{path}").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), serverRequest -> {
                    String str = serverRequest.pathVariable("path");
                    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).bodyValue(str)
                            .switchIfEmpty(ServerResponse.notFound().build());
                });
    }
}

浏览器请求 http://localhost:4990/hello/haha

haha

添加认证


/**
 * @author coding途中
 */

@Configuration
@EnableWebFluxSecurity
public class HelloWebfluxSecurityConfig  {

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("user")
                .password("user")
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        return http.authorizeExchange()
                .pathMatchers( "/hello/**").authenticated()
                .pathMatchers("/hello/login").permitAll()
                .anyExchange().authenticated()
                .and()
                .formLogin().and()
                .logout().and()
                .httpBasic().and()
                .csrf().disable()
                .build();
    }
}
  • 再次请求接口 浏览器请求 http://localhost:4990/hello/haha 此时浏览重定向到 http://localhost:4990/login

登陆页面

输入user/user 用户名密码后完成登陆。

再次浏览器请求 http://localhost:4990/hello/authenticate

authenticate

结束

识别下方二维码!回复: 入群 ,扫码加入我们交流群!

点赞是认可,在看是支持

Spring WebFlux 教程:如何构建反应式 Web 应用程序的更多相关文章

  1. Building Modern Web Apps-构建现代的 Web 应用程序

    Building Modern Web Apps-构建现代的 Web 应用程序 视频长度:1 小时左右 视频作者:Scott Hunter 和 Scott Hanselman 视频背景:Visual ...

  2. Spring Boot入门教程1、使用Spring Boot构建第一个Web应用程序

    一.前言 什么是Spring Boot?Spring Boot就是一个让你使用Spring构建应用时减少配置的一个框架.约定优于配置,一定程度上提高了开发效率.https://zhuanlan.zhi ...

  3. 实战基于Spring Boot 2的WebFlux和mLab搭建反应式Web

    Spring Framework 5带来了新的Reactive Stack非阻塞式Web框架:Spring WebFlux.作为与Spring MVC并行使用的Web框架,Spring WebFlux ...

  4. 利用memcached构建高性能的Web应用程序(转载)

    面临的问题 对于高并发高访问的Web应用程序来说,数据库存取瓶颈一直是个令人头疼的问题.特别当你的程序架构还是建立在单数据库模式,而一个数据池连接数峰 值已经达到500的时候,那你的程序运行离崩溃的边 ...

  5. 一步步构建.NET Core Web应用程序---基本项目结构

    前言 随着.NET Core日益成熟, 我作为C#&.NET体系中的一份子也加入了.NET Core 这一体系中,随着不断学习, 接触到的各种框架日益庞杂,接下来我会由一个新手的角度把整个基于 ...

  6. Spring Cloud教程(八)云原生应用程序

    Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线).分布式系统的协调导致了样板模式, 使用Spring Clo ...

  7. Building Modern Web Apps-构建现代的 Web 应用程序(一些感想)

    <iframe src="http://channel9.msdn.com/Series/MVA-China/Web20140611A01/player?h=540&w=960 ...

  8. 一步步构建.NET Core Web应用程序---仓储层,业务层的实现

    前言 上一篇文章介绍了整个项目的结构,接下来向大家介绍一下 我的 仓储及业务层具体的实现思路,如果有更好的实现方式,希望大家及时指出!!! 构建过程 一,数据访问 首先在 DataProvider 中 ...

  9. SpringBoot学习笔记(11):使用WebSocket构建交互式Web应用程序

    SpringBoot学习笔记(11):使用WebSocket构建交互式Web应用程序 快速开始 本指南将引导您完成创建“hello world”应用程序的过程,该应用程序在浏览器和服务器之间来回发送消 ...

随机推荐

  1. springboot国际化与@valid国际化支持

    springboot国际化 springboot对国际化的支持还是很好的,要实现国际化还简单.主要流程是通过配置springboot的LocaleResolver解析器,当请求打到springboot ...

  2. 了解常用数据库MySQL、Oracle、MongoDB

    本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net 注:转载文章 什么是数据库 简单的说,数据库(英文 Dtabase)就是一个存放数据的仓库,这个仓库是按照一定的数据结果( ...

  3. java基础——多维数组和稀疏数组

    多维数组 多维数组可以堪称数组的数组,比如二维数组就是一个特殊的一维数组,其中每一个元素都是一个以为数组 而且数组 int a[][]= new int[2][3]; public class Arr ...

  4. [BD] Sqoop

    什么是Sqoop 数据交换工具(ETL):RDBMS(MySQL.Oracle等)<-->Sqoop<-->HDFS(HBase.Hive等) 基于JDBC 执行数据交换时,本 ...

  5. (代替人类)很多操作都在Settings里面。 5.安装第三方库

    2020-02-01 pycharm 使用教程 LingSmart关注 0.0842020.02.07 15:08:50字数 1,394阅读 680 实在无聊,就来学习吧.学习pycharm的使用教程 ...

  6. Docker——安装部署

    前言 从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE. Docker CE 即社区免费版,Docker EE 即企业版,强调安 ...

  7. LVM 相关知识

    LVM 相关知识 一.示例图 二.概念 名词 全称 释义 PV Physical Volume 物理硬盘.硬盘分区或者RAID磁盘阵列,先要创建pv VG Volume Group 卷组建立在物理卷之 ...

  8. centos 7安装freescale交叉编译工具链

    方法1:可以直接下载gcc包,把文件夹放到/usr/local下即可,然后修改PATH环境变量,既可以使用 方法2:可以下载.rpm包,在本地进行安装,下载地址为(http://www.panduod ...

  9. Centos6.5 修改主机名(hostname)

    centos6需要修改两处:一处是/etc/sysconfig/network,另一处是/etc/hosts,只修改任一处会导致系统启动异常.首先切换到root用户. /etc/sysconfig/n ...

  10. GLSL着色器,来玩

    对实现动画的前端同学们来说,canvas可以说是最自由,最能全面控制的一个动画实现载体.不但能通过javascript控制点.线.面的绘制,使用图片资源填充:还能改变输入参数作出交互动画,完全控制动画 ...