01-hello
现在开始学习springboot中的webflux用法(基于2.0.5版本)。
响应式
先聊一聊响应式。首先看下面的代码:
int a=1;
int b=a+1;
System.out.print(“b=”+b) // b=2
a=10;
System.out.print(“b=”+b) // b=2
上面是一段很常见的代码,简单的赋值打印语句,但是这种代码有一个缺陷,那就是如果我们想表达的并不是一个赋值动作,而是b和a之间的关系,即无论a如何变化,b永远比a大1。那么可以想见,我们就需要花额外的精力去构建和维护一个b和a的关系。
而响应式编程的想法正是企图用某种操作符帮助你构建这种关系。它的思想完全可以用下面的代码片段来表达:
int a=1;
int b<=a+1; // <= 符号只是假设表示a和b之间关系的操作符
System.out.print(“b=”+b) // b=2
a=10;
System.out.print(“b=”+b) // b=11
这就是是响应式的思想,它希望有某种方式能够构建关系,而不是执行某种赋值命令。
我们为什么需要构建关系的代码而不是命令式的代码呢?如果你翻一翻自己正在开发的APP,你就能看到的每一个交互的页面其实内部都包含了一系列的业务逻辑。而产品的每个需求,其实也对应了一系列的业务逻辑相互作用。总之,我们的开发就是在构建一系列的业务逻辑之间的关系。你说我们是不是需要构建关系的代码?
说回响应式,前期由于真实的编程环境中并没有构建关系的操作符,主流的编程语言并不支持这种构建关系的方式,所以一开始响应式主要停留在想的层面,直到出现了Rx和一些其他支持这种思想的框架,才真正把响应式编程引入到了实际的代码开发中。
Rx是响应式拓展,即支持响应式编程的一种拓展,为响应式在不同语言中的实现提供指导思想。
响应式编程
响应式编程是一种通过异步和数据流来构建事务关系的编程模型。这里每个词都很重要,“事务的关系”是响应式编程的核心理念,“数据流”和“异步”是实现这个核心理念的关键。为了帮助大家理解这个概念,可以假设一个X业务,完成这个业务需要提前做好ABC三项工作。
此时事务指的就是完成ABC三项准备工作,并完成X业务这个整体的业务逻辑。
事务的关系指的就是完成X需要先完成ABC三个准备工作这样的关系。
数据流是事务之间沟通的桥梁。比如ABC三项任务完成以后需要通知X业务,X才能开始。通知最好的方式就是通过数据流。
但是,只有数据流是不能完全正确的构建出事务之间的关系的。我们依然需要异步编程。异步编程本身是有很多优点的,比如挖掘多核心CPU的能力,提高效率,降低延迟和阻塞等等。但实际上,异步编程也给我们构建事务的关系提供了帮助。
比如ABC三项任务之间并没有先后和依赖关系,相互独立,所以应该在不同的线程中执行三个任务,保证三个之间不会相互阻塞,
总的来说,异步和数据流都是为了正确的构建事务的关系而存在的。只不过,异步是为了区分出无关的事务,而数据流(事件流)是为了联系起有关的事务。
为什么引入响应式编程
响应式编程至少有如下好处:
在业务层面实现代码逻辑分离,方便后期维护和拓展
极大提高程序响应速度,充分发掘CPU的能力
帮助开发者提高代码的抽象能力和充分理解业务逻辑
Rx丰富的操作符会帮助我们极大的简化代码逻辑
经过上面简单的介绍,下面进入正题。
Spring Boot 2.0 WebFlux
了解 WebFlux,首先了解下什么是 Reactive Streams。Reactive Streams 是 JVM 中面向流的库标准和规范:
处理可能无限数量的元素
按顺序处理
组件之间异步传递
强制性非阻塞背压(Backpressure)
Backpressure(背压)
背压是一种常用策略,使得发布者拥有无限制的缓冲区存储元素,用于确保发布者发布元素太快时,不会去压制订阅者。
Reactive Streams(响应式流)
一般由以下组成:
发布者:发布元素到订阅者
订阅者:消费元素
订阅:在发布者中,订阅被创建时,将与订阅者共享
处理器:发布者与订阅者之间处理数据
响应式编程
有了 Reactive Streams 这种标准和规范,利用规范可以进行响应式编程。那再了解下什么是 Reactive programming 响应式编程。响应式编程是基于异步和事件驱动的非阻塞程序,只是垂直通过在 JVM 内启动少量线程扩展,而不是水平通过集群扩展。这就是一个编程范例,具体项目中如何体现呢?
响应式项目编程实战中,通过基于 Reactive Streams 规范实现的框架 Reactor 去实战。Reactor 一般提供两种响应式 API :
Mono:实现发布者,并返回 0 或 1 个元素
Flux:实现发布者,并返回 N 个元素
Spring Webflux
Spring Boot Webflux 就是基于 Reactor 实现的。Spring Boot 2.0 包括一个新的 spring-webflux 模块。该模块包含对响应式 HTTP 和 WebSocket 客户端的支持,以及对 REST,HTML 和 WebSocket 交互等程序的支持。一般来说,Spring MVC 用于同步处理,Spring Webflux 用于异步处理。
Spring Boot Webflux 有两种编程模型实现,一种类似 Spring MVC 注解方式,另一种是使用其功能性端点方式。注解的会在第二篇文章讲到,下面快速入门用 Spring Webflux 功能性方式实现。
Spring Boot 2.0 WebFlux 特性
常用的 Spring Boot 2.0 WebFlux 生产的特性如下:
响应式 API
编程模型
适用性
内嵌容器
Starter 组件
还有对日志、Web、消息、测试及扩展等支持。
响应式 API
Reactor 框架是 Spring Boot Webflux 响应库依赖,通过 Reactive Streams 并与其他响应库交互。提供了 两种响应式 API:Mono 和 Flux。一般是将 Publisher 作为输入,在框架内部转换成 Reactor 类型并处理逻辑,然后返回 Flux 或 Mono 作为输出。
适用性
上图就很明确了,WebFlux 和 MVC 有交集,方便大家迁移。但是注意:
MVC 能满足场景的,就不需要更改为 WebFlux。
要注意容器的支持,可以看看下面内嵌容器的支持。
微服务体系结构,WebFlux 和 MVC 可以混合使用。尤其开发 IO 密集型服务的时候,选择 WebFlux 去实现。
编程模型
Spring 5 web 模块包含了 Spring WebFlux 的 HTTP 抽象。类似 Servlet API , WebFlux 提供了 WebHandler API 去定义非阻塞 API 抽象接口。可以选择以下两种编程模型实现:
注解控制层。和 MVC 保持一致,WebFlux 也支持响应性 @RequestBody 注解。
功能性端点。基于 lambda 轻量级编程模型,用来路由和处理请求的小工具。和上面最大的区别就是,这种模型,全程控制了请求 - 响应的生命流程
内嵌容器
跟 Spring Boot 大框架一样启动应用,但 WebFlux 默认是通过 Netty 启动,并且自动设置了默认端口为 8080。另外还提供了对 Jetty、Undertow 等容器的支持。开发者自行在添加对应的容器 Starter 组件依赖,即可配置并使用对应内嵌容器实例。
但是要注意,必须是 Servlet 3.1+ 容器,如 Tomcat、Jetty;或者非 Servlet 容器,如 Netty 和 Undertow。
Starter 组件
跟 Spring Boot 大框架一样,Spring Boot Webflux 提供了很多 “开箱即用” 的 Starter 组件。Starter 组件是可被加载在应用中的 Maven 依赖项。只需要在 Maven 配置中添加对应的依赖配置,即可使用对应的 Starter 组件。例如,添加 spring-boot-starter-webflux 依赖,就可用于构建响应式 API 服务,其包含了 Web Flux 和 Tomcat 内嵌容器等。
开发中,很多功能是通过添加 Starter 组件的方式来进行实现。那么,Spring Boot 2.x 常用的 Starter 组件有哪些呢?
Spring Boot 2.0 WebFlux 组件
Spring Boot WebFlux 官方提供了很多 Starter 组件,每个模块会有多种技术实现选型支持,来实现各种复杂的业务需求:
Web:Spring WebFlux
模板引擎:Thymeleaf
存储:Redis、MongoDB、Cassandra,不支持 MySQL
内嵌容器:Tomcat、Jetty、Undertow
经过上面枯燥的理论知识,下面开始写代码,代码非常简单,首先打开网址https://start.spring.io,构建如下项目:
注意上面选择的版本和组件。
选择好以后,下载配置的项目代码。然后打开,首先打开pom文件,可以看到依赖配置如下:
spring-boot-starter-webflux 依赖,是我们核心需要学习 webflux 的包,里面默认包含了 spring-boot-starter-reactor-netty 、spring 5 webflux 包,也就是说默认是通过 netty 启动的。
reactor-test、spring-boot-starter-test 两个依赖搭配是用于单元测试。
编写处理器类 Handler
首先编写功能处理类。新建城市(City)例子的处理类 CityHandler,代码如下:
ServerResponse 是对响应的封装,可以设置响应状态、响应头、响应正文。比如 ok 代表的是 200 响应码、MediaType 枚举是代表这文本内容类型、返回的是 String 的对象。
这里用 Mono 作为返回对象,是因为返回包含了一个 ServerResponse 对象,而不是多个元素。
编写路由器类 Router
然后编写路由器类。新建城市(City)例子的路由类 CityRouter,代码如下:
RouterFunctions 对请求路由处理类,即将请求路由到处理器,这里将一个 GET 请求 /hello 路由到处理器 cityHandler 的 helloCity 方法上。跟 Spring MVC 模式下的 HandleMapping 的作用类似。
RouterFunctions.route(RequestPredicate, HandlerFunction) 方法,对应的入参是请求参数和处理函数,如果请求匹配,就调用对应的处理器函数。
到这里一个简单的服务就写好了,下面怎么运行该服务。
启动运行项目
一个简单的 Spring Boot Webflux 工程就开发完毕了,下面运行工程验证下。在 IDE 中执行 Application 类启动,任意正常模式或者 Debug 模式,可以在控制台看到成功运行的输出:
可以看到,确实是 Netty 启动的。
打开浏览器,访问 /hello 地址,会看到如图所示的返回结果:
总结
本文主要讲了 Spring Boot 2.0 WebFlux 背景和快速入门使用,用的是基于功能性端点去创建一个服务,但这个有点代码偏多。下一课一个 CRUD 我们使用注解控制层,让开发更方便。
原文:https://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9b3d7966c5ae1087033
代码:https://gitee.com/blueses/springboot-webflux