艿艿连肝了几个周末,写了一篇贼长的 Spring 响应式 Web 框架 WebFlux!市面第二完整~...

点击上方“芋道源码”,选择“设为星标

做积极的人,而不是积极废人!

源码精品专栏

 

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/WebFlux/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述

  • 2. 快速入门

  • 3. 测试接口

  • 4. 全局统一返回

  • 5. 全局异常处理

  • 7. Servlet、Filter、Listener

  • 8. Cors 跨域

  • 9. 集成响应式的 MongoDB

  • 10. 集成响应式的 Redis

  • 11. 集成响应式的 Elasticsearch

  • 12. 整合响应式的 JPA

  • 13. 整合响应式的 R2DBC 和事务

  • 14. 其他内容

  • 666. 彩蛋


本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-27 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

友情提示:Reactive Programming ,翻译为反应式编程,又称为响应式编程。本文,我们统一使用响应式。不过,比较正确的叫法还是反应式。

Spring Framework 5 在 2017 年 9 月份,发布了 GA 通用版本。既然是一个新的大版本,必然带来了非常多的改进,其中比较重要的一点,就是将响应式编程带入了 Spring 生态。又或者说,将响应式编程“真正”带入了 Java 生态之中。

在此之前,相信绝大多数 Java 开发者,对响应式编程的概念是非常模糊的。甚至说,截止到目前 2019 年 11 月份,对于国内的 Java 开发者,也是知之甚少。

对于我们来说,最早看到的就是 Spring5 提供了一个新的 Web 框架,基于响应式编程的 Spring WebFlux 。至此,SpringMVC 在“干掉” Struts 之后,难道要开始进入 Spring 自己的两个 Web 框架的双雄争霸?

实际上,WebFlux 在出来的两年时间里,据艿艿所了解到的情况,鲜有项目从采用 SpringMVC 迁移到 WebFlux ,又或者新项目直接采用 WebFlux 。这又是为什么呢?

艿艿:V2EX 上还有这样一个讨论 《现在有公司在使用 Spring Boot 2.0 的 WebFlux 吗?》 。

响应式编程,对我们现有的编程方式,是一场颠覆,对于框架也是。

  • 在 Spring 提供的框架中,实际并没有全部实现好对响应式编程的支持。例如说,Spring Transaction 事务组件,在 Spring 5.2 M2 版本,才提供了支持响应式编程的 ReactiveTransactionManager 事务管理器。

  • 更不要说,Java 生态常用的框架,例如说 MyBatis、Jedis 等等,都暂未提供响应式编程的支持。

所以,WebFlux 想要能够真正普及到我们的项目中,不仅仅需要 Spring 自己体系中的框架提供对响应式编程的很好的支持,也需要 Java 生态中的框架也要做到如此。例如说:

艿艿:???? Java 框架存在大量基于 ThreadLocal 线程变量,实现参数的透传,改造的成本,实际是不小的。

当然,即使如此,这也并不妨碍我们来对 WebFlux 进行一个小小的入门。毕竟,响应式编程这把火,终将熊熊燃起,烧死那些异性恋。哈哈哈~

艿艿:下面的会涉及比较多的概念,不想看的胖友,直接跳到 「2. 快速入门」 小节,直接开始 WebFlux 的入门。

1.1 响应式编程

我们先简单来了解下响应式编程的相关姿势,以保证能够看懂 WebFlux 入门的代码示例,哈哈哈~

维基百科对响应式编程定义如下:

FROM https://en.wikipedia.org/wiki/Reactive_programming

Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. This means that it becomes possible to express static (e.g. arrays) or dynamic (e.g. event emitters) data streams with ease via the employed programming language(s).

反应式编程是一种异步编程范式,它关注数据流和变化的传播。这意味着可以通过使用编程语言轻松地表示静态(如数组)或动态(如事件发射器)数据流。

Spring 官方文档对响应式编程定义如下:

FROM https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html#web-reactive-programming

In plain terms reactive programming is about non-blocking applications that are asynchronous and event-driven and require a small number of threads to scale vertically (i.e. within the JVM) rather than horizontally (i.e. through clustering).

简单地说,响应式编程是关于非阻塞应用程序的,这些应用程序是异步的、事件驱动的,并且需要少量的线程来垂直伸缩(即在 JVM 中),而不是水平伸缩(即通过集群)。

???? 两个看起来都不很易懂。不过如果胖友看过 Netty 框架的介绍,会发现跟 Spring 的描述非常相像。定义如下:

FROM https://www.oschina.net/p/netty

Netty 是一个 Java 开源框架。Netty 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

是不是都看到了异步 + 事件驱动。本质上,Netty 也是有基于响应式编程的思想。所以在下文中,我们会看到,可以使用 Netty 作为 WebFlux 的服务器。

哔哔了这么多,艿艿来用简单但不完全精准的语言尝试下。以后端 API 请求的处理来举例子。

  • 在现在主流的编程模型中,请求是被同步阻塞处理完成,返回结果给前端。

  • 在响应式的编程模型中,请求是被作为一个事件丢到线程池中执行,等到执行完毕,异步回调结果给主线程,最后返回给前端。

通过这样的方式,主线程(实际是多个,这里只是方便描述哈)不断接收请求,不负责直接同步阻塞处理,从而避免自身被阻塞。

1.2 Reactor 框架

在 Java 生态中,提供响应式编程的框架主要有 Reactor、RxJava、JDK9 Flow API 。

那么,Spring 会选择哪个框架作为其响应式编程的基础呢?

  • 首先,可以排除 JDK9 Flow API ,因为 Spring5 要支持 JDK8 版本开始。

  • 其次,Reactor 是 Spring 公司 Pivotal(咳咳咳,2019 年竟然被 VMWare 收购了)是开源的框架,所以必然是“强强联合”,嘿嘿。

如果胖友想要了解 Reactor 和 RxJava 的对比,可以看看 《八个层面比较 Java 8, RxJava, Reactor》 文章,挺详细的。

让我们一起来看看 Reactor 官方对自己的介绍:

FROM https://projectreactor.io/

Reactor is a fourth-generation Reactive library for building non-blocking applications on the JVM based on the Reactive Streams Specification

Reactor 是一个第四代响应式编程框架,用于构建非阻塞 JVM 应用程序,基于 Reactive Streams Specification 来实现。

Reactor Operators and Schedulers can sustain high throughput rates on the order of 10's of millions of messages per second.

Reactor 的操作和调度可以提供每秒千万条消息的高吞吐量。

Plus its low memory footprint should go under most of the radars.

再加上它的低内存占用,应该在大多数雷达(radars)之下。咳咳咳,这个 radars 怎么翻译。

简单来说,Reactor 说是一个响应式编程框架,又快又不占用内存的那种。????

关于 Reactor 的使用,这里艿艿就不过多介绍,感兴趣的胖友,可以看看 《使用 Reactor 进行反应式编程》 文章。如下是对其中的一段内容的节选并修改:

Reactor 有两个非常重要的基本概念:

  • Flux ,表示的是包含 0 到 N 个元素的异步序列。当消息通知产生时,订阅者(Subscriber)中对应的方法 #onNext(t), #onComplete(t)#onError(t) 会被调用。

  • Mono 表示的是包含 0 或者 1 个元素的异步序列。该序列中同样可以包含与 Flux 相同的三种类型的消息通知。

  • 同时,Flux 和 Mono 之间可以进行转换。例如:

    • 对一个 Flux 序列进行计数操作,得到的结果是一个 Mono<Long> 对象。

    • 把两个 Mono 序列合并在一起,得到的是一个 Flux 对象。

???? 其实,可以先暂时简单把 Mono 理解成 Object ,Flux 理解成 List 。嘿嘿~

1.3 Spring WebFlux

Spring 官方文档对 Spring WebFlux 介绍如下:

FROM https://docs.spring.io/spring-framework/docs/5.0.0.BUILD-SNAPSHOT/spring-framework-reference/html/web-reactive.html

Spring Framework 5 includes a new spring-webflux module. The module contains support for reactive HTTP and WebSocket clients as well as for reactive server web applications including REST, HTML browser, and WebSocket style interactions.

Spring Framework 5 提供了一个新的 spring-webflux 模块。该模块包含了:

  • 对响应式支持的 HTTP 和 WebSocket 客户端。

  • 对响应式支持的 Web 服务器,包括 Rest API、HTML 浏览器、WebSocket 等交互方式。

On the server-side WebFlux supports 2 distinct programming models:

  • Annotation-based with @Controller and the other > annotations supported also with Spring MVC

  • Functional, Java 8 lambda style routing and handling

在服务端方面,WebFlux 提供了 2 种编程模型(翻译成使用方式,可能更易懂):

  • 方式一,基于 Annotated Controller 方式实现:基于 @Controller 和 SpringMVC 使用的其它注解。???? 也就是说,我们大体上可以像使用 SpringMVC 的方式,使用 WebFlux 。

  • 方式二,基于函数式编程方式:函数式,Java 8 lambda 表达式风格的路由和处理。???? 可能有点晦涩,晚点我们看了示例就会明白。

Both programming models are executed on the same reactive foundation that adapts non-blocking HTTP runtimes to the Reactive Streams API.

这两个编程模型,都是在同一个响应式基础(foundation)上执行的,该基础将非阻塞 HTTP 运行时(runtime)适配成响应式 API 。???? 简单来说,就是将原有的 API ,使用 Reactor 封装成响应式 API ,让我们开发者使用更加便捷。

The diagram below shows the server-side stack including traditional, Servlet-based Spring MVC on the left from the spring-webmvc module and also the reactive stack on the right from the spring-webflux module.

下图显示了服务端的技术栈,左侧是 spring-webmvc 模块中传统的、基于 Servlet 的 Spring MVC ,右侧是 spring-webflux 模块中的响应式技术栈。

webflux-overview
  • ???? 仔细看第一层的两个框框,分别是上面提到的 WebFlux 的两种编程模型。表达的是 SpringMVC 不支持 Router Functions 方式,而 WebFlux 支持。

WebFlux can run on Servlet containers with support for the Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as Netty and Undertow.

WebFlux 可以运行在:

  • 支持 Servlet 3.1 非阻塞 IO API 的 Servlet 容器上

  • 也可以运行在支持异步运行时的,例如说 Netty 或者 Undertow 上

Each runtime is adapted to a reactive ServerHttpRequest and ServerHttpResponse exposing the body of the request and response as Flux, rather than InputStream and OutputStream, with reactive backpressure.

每一个运行时(runtime)适用于将响应式的 ServerHttpRequest 和 ServerHttpResponse 中 request 和 response 的 body 暴露成 Flux<DataBuffer> 对象,而不是 InputStream  和 InputStream  对象,可用于响应式中的背压(backpressure)。???? 这段有点晦涩,简单来说:

  • 对于 Servlet 来说, ServletRequest#getInputStream() 方法,获得请求的主体内容返回的是 InputStream 对象。

  • 对于 WebFlux 来说,ServerHttpRequest#getBody() 方法,获得请求的主体内容返回的是 Flux<DataBuffer> 对象。

REST-style JSON and XML serialization and deserialization is supported on top as a Flux<Object>, and so is HTML view rendering and Server-Sent Events.

REST 风格 API 使用到的 JSON 和 XML 序列化和反序列化,需要提供对 Flux<Object> 的支持。对于 HTML 渲染,和 SSE 也要提供对 Flux<Object> 的支持。

???? 咳咳咳,看完了这一大段,是不是突然有点想捶死艿艿,说的什么 XX 玩样啊!其实,在我们初学 SpringMVC 的时候,也是一脸懵逼的学完。随着我们对 SpringMVC 的日趋熟练,逐步对其提供的组件、原理、源码慢慢熟悉。所以,对于我们来说,WebFlux 乃至响应式编程来说,都是足够新颖的知识,我们要抱着空杯心态,「Stay Hungry, Stay Foolish」 。

如果胖友的时间比较充分,可以选择把 《Spring 文档 —— Web on Reactive Stack》 仔细看看,详尽的介绍了 Spring 在 Web 方面,响应式相关的技术栈。

虽然说上面我们在介绍 WebFlux ,把它搞的很复杂,实际在快速入门使用它,还是非常简单的。下面,开始让我们开始愉快的快速入门下~

艿艿:考虑到艿艿之前已经写了 《芋道 Spring Boot SpringMVC 入门》 文章,所以本文我们提供的示例,尽量覆盖到在 SpringMVC 提到的内容。

当然,很多相似的概念,艿艿也不重复介绍,不然显得我老啰嗦了。

2. 快速入门

示例代码对应仓库:lab-27-webflux-01 。

本小节,我们会使用 spring-boot-starter-webflux 实现 WebFlux 的自动化配置。然后实现用户的增删改查接口。接口列表如下:

请求方法 URL 功能
GET /users/list 查询用户列表
GET /users/get 获得指定用户编号的用户
POST /users/add 添加用户
POST /users/update 更新指定用户编号的用户
POST /users/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值