介绍
最初的web框架包含在Spring框架、SpringMVC框架中目的是为了构建ServletAPI和Servlet容器。
Spring5.0版本新增Webflux模块,Webflux支持http://www.jianshu.com/p/2c4799fa91a4①、服务器容器上运行(e.g:Jetty,Undertow和Servlet3.1+)
Spring-webmvc和Spring-webflux这两个web镜像框架可以共存于Spring中。每个模块是可选的,在某些情况下,应用可以使用一个或其他模块——(例如:响应式WebClient SpringMVC控制器)
为什么使用新的SpringWeb框架?
一方面是需要一个非阻塞的网络堆栈处理小数量线程和规模较少的硬件资源的并发性,Servlet3.1提供了一个处理非阻塞IO的API,但是,使用它会远离一些契约是同步的(例:filter和servlet)或者阻塞\(getParameter、getPart\)的API。在任何非阻塞运行期间这是作为基础为服务建立新的API的目的,这对于服务器例如Netty建立一个异步、非阻塞的空间
另一方面是因为函数式编程。就像在Java5添加了注解一样——例如:带注解的Rest控制器或者单元测试,在Java8中新增了函数功能的lambda表达式。这是一个有利于非阻塞应用的推广和延续良好风格—使CompletableFuture 、ReactiveX允许声明异步处理的逻辑。在Java8中兼容SpringWflux提供带注解的功能网络端点
响应式:What和Why
我们提到了非阻塞和函数,但是为什么是响应式的以及所说的到底是什么?
“Reactive”这一术语指的是**在对I / O事件做出反应的网络组件和对鼠标事件做出反应的UI控制器之间建立的编程模型**,在这种层意义上说非阻塞是响应式的,因为没有被阻塞,当数据完成或者可用时,是处于响应-通知模式的。
还有另外一个重要的机制是我们在Spring团队中"reactive"和非阻塞背压是联系在一起的。在阻塞调用中,对于必要的代码,背压是自然形式的一种回压,迫使调用者等待
例如一个存储数据库——作为发布者,可以在一个HTTP服务器中生产数据。作为订阅者,可以写入响应。
**响应流的主要目的是允许控制发布者过快或者过慢的生成数据**
常见的问题:如果一个发布者生成数据的速率无法慢下来呢?
响应流的目的是建议一个机制和边界。如果一个发布者无法慢下来,那么它必须决定是缓冲、降级或者失败。
响应式流库API
响应式流在互用性中起着重要作用,是库和基础组件的兴趣所在,但是由于级别太低对应用API的作用不大。应用程序需要一个更高的层次和更丰富的功能性API来编写异步逻辑——类似Java中的StreamAPI功能。但不局限与集合,这就是Reactive所担任的角色。
Reactor是SpringWebflux响应式流库。它提供一组Mono和FluxAPI通过一组丰富的操作符处理0-1或者0-N的数据序列。与运营商ReactiveX的词汇类似。Reactor是一个活性流库,因此它所有的操作都支持非阻塞背压(non-blocking back pressure)。因为是由Spring合作开发的所以主要针对Java服务端
虽然WebFlux需要Reactor类库作为核心依赖,但它可以通过响应流和其他流响应库互相操作。
通常,Webflux允许通过特定格式的Publisher(发布者)作为输入,在内部将它与Reactor类型相适应,并且使用这些数据,最后返回通量(either Flux)或Mono作为输出。因此,你可以通过任何一个Publisher作为输入,并且对输出进行操作。为了便于与另外的响应式库使用,你需要调整相应的输出,只要符合实际。—— 例如:注解控制器可以让Webflux透明的适应RxJava或者其他响应式库
编程模型
spring-web模块内包含SpringWebflux的基础——HTTP抽象类、响应式流服务端适配器、Reactive编解码器、一个核心的WebAPI(其作用可以与ServletAPI相比较,但具有非阻塞语义)
SpringWebflux提供了2种编程模型
注解控制器(Annotated Controllers)—— 符合SpringMVC格式,并且基于spring-web模块的相同注解。SpringMVC和Webflux都可以支持响应式(Reactor, RxJava)返回类型,因此不容易区分它们,它们之间有个明显的区别是Webflux支持Reactive的@RequestBody参数
功能性端点(Functional Endpoints) 基于lambda表达式、轻量级、函数编程模型。可以把它看作一个小的库或者一组实用程序,应用程序可以用来路由和处理请求。与注解控制器最大的区别在于,应用程序负责一个请求开始到结束,说明意图完成调用。
选择Web框架
是否应该使用Webflux或者SpringMVC?让我们来讨论几个不同的观点
如果你有一个SpringMVC项目运行的很好,则不需要改变。命令式编程是写、理解、调试代码的最佳方式。从历史上看大多数编程是阻塞的
如果您已经购买了非阻塞的web堆栈,Spring WebFlux提供了与该空间中的其他相同的执行模型好处,还提供服务器容器的选择——Netty, Tomcat, Jetty, Undertow, Servlet 3.1。编程模型的选择——带注释的控制器和功能性端点和响应库——Reactor,RxJava或其他。
如果您对一个轻量级的、功能强大的web框架感兴趣,可以使用Java 8 lambdas或Kotlin,然后再使用Spring WebFlux功能性端点。
对于较小的应用程序或微服务来说,这也是一个不错的选择,因为它可以从得到更好的控制和更多的透明性。
在微服务体系结构中,您可以使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端点混合应用程序。在这两种框架中支持相同的基于注解的编程模型,可以更容易地重用知识,同时为工作选择合适的工具。
一个最客观的检查方案就是检查它的依赖项,如果使用了阻塞持久性api(JPA、JDBC)或网络api,那么Spring MVC至少是通用架构的最佳选择。从技术上讲,Reactor和RxJava两者在单独的线程执行阻塞调用是可行的,但不会充分运用非阻塞调用的网络堆栈
如果你在一个大的团队,请记住在转向非阻塞、功能和声明性编程的过程中,曲线学习。在没有完全交换机的情况下,一种实用的方法是使用响应式WebClient。除此之外,从小事做起,学会衡量。希望多数应用的转变是不必要的。
如果你想知道有什么好处,首先学习非阻塞I / O是如何工作的(例如:并发在单线程的Node.js中并不矛盾)和它的效果。标记线是“用更少的硬件规模”,但网络I / O效果无法保证
常见问题:如何用于Tomcat和Jetty栈?
Tomcat和Jetty是非阻塞的,从Servlet3.1之后,它是添加阻塞facade的Servlet API,但是它的使用需要注意避免其他的同步和阻塞部分。出于这个原因,Spring的web响应堆栈有一个低级别的适配器可以连接到响应式流,API不会暴露出来。
SpringBoot2 默认Webflux来使用Netty,因为Netty在异步、非阻塞空间中使用更广泛,同时也提供了可以共享资源的客户端和服务器。相较而下,Servlet 3.1非阻塞I / O并没有看到太多的使用,Spring WebFlux 采纳了一条使用路径
SpringBoot默认服务器的选择主要关于"开箱即用"的体验.同样,应用程序仍然可以选择任何其他支持的服务器,这些也对性能进行了高度优化,完全非阻塞模式,并适应了响应式背压。在Spring Boot中,转换是很简单的。
性能和规模
性能有许多特点和含义。响应式和非阻塞特性通常不会使应用程序运行得更快。它们可以在某些情况下,例如,可以使用WebClient并行执行远程调用。它需要更多的工作来做非阻塞的事情,所以稍微增加所需的处理时间。
响应式和非阻塞的预期关键好处是规模小,固定数量的线程和更少的内存。这使得应用程序具有弹性负载特性,并且具有一种可观测的收缩性。为了观察这些好处,你需要有一些包括缓慢的和不可预知的网络I / O延迟。这就是响应式堆栈开始显示其优势的地方,差异可能是巨大的。
①响应式背压:背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略,简而言之,背压是流速控制的一种策略。