Spring WebFlux是Spring从5.0开始提供的,由于最近在研究Spring Boot,正好研究到了Spring Boot中的WebFlux所以就看了下Spring官方文档,后来发现只有英文的,不太方便,所以根据官网进行了翻译.我不是一个专业的翻译人员,第一写,希望大家多多鼓励.如果有翻译不太恰当的地方,希望大家给指正.
为什么需要Spring WebFlux?
一个答案是需要一个只使用了少量线程和更低硬件资源的非阻塞web栈来处理并发问题.Servlet3.1为非阻塞IO提供了一些API.然而使用他就意味着与那些同步和阻塞的Servlet API远离了.
这是新的能服务于跨非阻塞运行环境的普遍API产生的动机.这也是很重要的,有像Netty这样的在异步和非阻塞上做的比较好的服务器的支持
另一个答案是函数式程序设计.虽然Java5中增加的注解(RestController和UnitTest)和Java8中增加的Lambda表达式都为Java里的函数式编程创造了机会.对于非阻塞应用程序和允许声明式创建异步逻辑的后继式API(通过CompletableFuture和ReactiveX来普及)来说是个福利.在程序设计模型级别上,Java8启用了Spring WebFlux来提供函数式web endpoints与注解Controller.
我们接触了"非阻塞"和"函数式",那reactive是什么意思呢?
"reactive"这个术语指的是:围绕着对改变做出响应的程序设计模型---网络组件对IO事件做出响应,UIController对鼠标事件做出响应等等.在那种情况下,非阻塞取代了阻塞是响应式的,我们正处于响应模式中,当操作完成和数据变得可用的时候发起通知.
还有另一个重要的机制那就是我们在spring team里整合"reactive"以及非阻塞式背压机制.在同步里,命令式的代码,阻塞式地调用服务为普通的表单充当背压机制强迫调用者等待.在非阻塞式编程中,控制事件的频率就变得很重要防止快速的生产者不会压垮他的目的地.
Reactive Streams 是一个定义了使用背压机制的异步组件之间交互设计的小型说明书(在Java9中也采纳了).例如,一个数据仓库(可以看做Publisher)可以生产数据,然后HTTP Server(看做订阅者)可以写入到响应里.Reactive Streams的主要目的是让订阅者可以控制生产者产生数据的速度有多快或有多慢.
这里有个问题,如果生产者不能降速怎么办?我们要明白,Reactive Stream的目的只是简历一个机制和边界.如果生产者不能降速,就必须决定是否要开启缓冲,放弃或失败.
Reactive Streams 在互操作性上扮演了一个很重要的角色.类库和基础设施组件虽然有趣,但对于应用程序API来说却用处甚少,因为他们太底层了.应用程序需要一个更高级别更丰富的函数式API来编写异步逻辑---和Java8里的StreamAPI很类似,不过不仅仅是为集合做准备的.
Reactor 是为SpringWebFlux选择的一个reactive类库.它提供了Mono和Flux类型的API来处理0..1(Mono)和0..N(Flux)数据序列化通过一组丰富的操作集和ReactiveX vocabulary of operators对齐.Reactor 是一个Reactive Streams类库,所以他所有的操作都支持非阻塞背压机制.Reactor强烈地聚焦于Server端的Java.他在发展上和Spring有着紧密的协作.
WebFlux要求Reactor作为一个核心依赖,但凭借Reactive Streams也可以和其他的reactive libraries一起使用.一般来说,一个WebFlux API 接收一个Publisher作为输入,转换给一个内置的Reactor类型来使用,最后返回一个Flux或一个Mono作为输出.所以,你可以批准任何的Publisher作为输入,你可以应用操作在输出上,但你因为你使用了其他的reactive library所以你需要进行转换.只要可行(例如,注解controllers),WebFlux可以在使用RXJava和另一个reactive library之间透明的改变.看Reactive Libraries获取更多地细节.
Spring-Web模块包含了由SpringWebFlux构成的响应式的根基.包含了HTTP的抽象,Reactive 为服务端提供支持的Streams适配器,codecs和一个可以和Servlet API相比的使用了非阻塞约定的WebHandler API核心.
在那个基础上,Spring WebFlux提供了两种程序设计模型以供选择:
Annotated Controllers: 与Spring MVC并存的并且基于同样从spring-web模块而来的注解。无论是SpringMVC还是WebFlux都支持响应式的(Reactor和RXJava)返回类型.就结果而言,讲出他们之间的区别并不是很容易.一个明显的区别是WebFlux也支持响应式的@RequestBody 参数.
Functional Endpoints: 基于Lambda的,轻量级的和函数式程序设计模型.你可以把这个看作是一个小的应用程序可以用来路由和处理请求的类库或是一组实用的集合.使用注解Controller一个很大的不同就是应用程序负责从开始到结尾的请求处理,而不是通过注解和回调声明intent(这个词不知道怎么翻译合适).
Spring MVC还是 WebFlux?
一个很自然的问题要问,但要建立一个不完全二分法.实际上,扩展可用范围的情况下两者是可以一起工作的.这两者被设计得彼此之间是有一定的连续性和一致性的,他们可以被并列使用,一方可以从另一方中获益和反馈.以下的图显示了这两者的联系,他们的共性以及他们各自特有的一些支持:
我们建议你考虑以下具体的几点:
Spring WebFlux在Tomcat,Jetty,Servlet3.1+容器上被支持的和非Servlet运行环境像Netty和Undertow一样的好.所有的服务器都会被转换为更底层的,共性的API以便更高级别的程序设计模型能被跨服务器地支持.
Spring WebFlux没有内置的配置来启动和停止一个服务器.然而,从Spring 配置和WebFlux infrastructure和run it 中使用少量代码进行集成是很容易的.
Spring Boot有一个自动完成这些步骤的WebFlux Starter.该starter默认使用的是Netty,但通过改变Maven或Gradle的依赖来切换到tomcat,Jetty或Undertow是非常容易的.Spring Boot默认使用Netty是因为在异步和非阻塞方面使用的非常广泛,并且可以在客户端和服务端共享资源.
Tomcat和Jetty无论是SpringMVC还是WebFlux都可以使用.然而,要记住,他们使用这些服务器的方式是非常不一样的.Spring MVC主要依赖于Servlet的阻塞I/O,并且在需要是让应用程序直接使用Servlet API.Spring WebFlux依赖的是Servlet3.1非阻塞I/O,使用的是在底层进行了转换的Servlet API,而不是直接使用那些已暴露出来的接口.
对于Undertow来说,Spring WebFlux直接使用Undertow的APIs,并没有使用Servlet API.
性能这个词有很多特征和含义.Reactive 和非阻塞通常不会使应用程序运行地更快.在某些场景下,他们也可以.(例如,在并行条件下使用WebClient来执行远程调用的话).整体来说,非阻塞方式可能需要做更多的工作并且他也会稍微增加请求处理的时间.
对reactive和非阻塞好处的预期关键在于使用小,固定的线程数和更少的内存来扩展的能力.这使应用程序在加载的时候更加有弹性,因为他们以一种更可以预测的方式扩展.然而为了看到这些好处,你需要一些延迟(包括比较慢的不可预知的网络I/O).那是响应式堆栈开始显示他力量的地方,并且这些不同是非常吸引人的.
Spring MVC和Spring WebFlux都支持注解Controllers,但他们在并发模型和对阻塞和线程的默认呈现(assumptions)上是非常不同的.
在Spring MVC(和通用的servlet应用)中,都假设应用程序是阻塞当前线程的(例如,远程调用),并且出于这个原因,servlet容器处理请求的期间使用一个巨大的线程池来吸收潜在的阻塞.
在Spring WebFlux(和非阻塞服务器)中,假设应用程序是非阻塞的,所以,非阻塞服务器使用小的,固定代销的线程池(event loop workders)来处理请求.
"弹性伸缩"和"小数量的线程"或许听起来矛盾,但是对于不会阻塞当前线程(用依赖回调来取代)意味着你不需要额外的线程,因为非阻塞调用给处理了.
调用一个阻塞API
要是你需要使用阻塞库怎么办?Reactor和RxJava都提供了publishOn操作用一个不同的线程来继续处理.那意味着有一个简单的脱离舱口(一个可以离开非阻塞的出口).然而,请牢记,阻塞API对于并发模型来说不太合适.
易变的状态
在Reactor和RxJava里,你通过操作符生命逻辑,在运行时在不同的阶段里,都会形成一个进行数据序列化处理的管道.这样做的一个主要好处就是把应用程序从不同的状态保护中解放了出来,因为管道中的应用代码是绝不会被同时调用的.
线程模型
在运行了一个使用Spring WebFlux的服务器上,你期望看到什么线程呢?
配置
Spring 框架不为启动和停止servers提供支持.为了给一个服务器配置一个线程模型,你需要使用指定的服务器配置APIs,或者,你使用Spring Boot,检查Spring Boot为每个服务器提供的配置选项.你可以直接配置WebClient.对于所有其他的库,可以参考他们各自的文档.
如果喜欢,可以给个好评鼓励一下,嘿嘿.稍后会发布第二篇《Reactive Core》,敬请期待