Spring 5.1.2核心技术之WebFlux(一)

1 篇文章 0 订阅
1 篇文章 0 订阅

       Spring WebFlux是Spring从5.0开始提供的,由于最近在研究Spring Boot,正好研究到了Spring Boot中的WebFlux所以就看了下Spring官方文档,后来发现只有英文的,不太方便,所以根据官网进行了翻译.我不是一个专业的翻译人员,第一写,希望大家多多鼓励.如果有翻译不太恰当的地方,希望大家给指正.

1.1 概论

       为什么需要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.

1.1.1 "Reactive"的定义

        我们接触了"非阻塞"和"函数式",那reactive是什么意思呢?
       "reactive"这个术语指的是:围绕着对改变做出响应的程序设计模型---网络组件对IO事件做出响应,UIController对鼠标事件做出响应等等.在那种情况下,非阻塞取代了阻塞是响应式的,我们正处于响应模式中,当操作完成和数据变得可用的时候发起通知.

       还有另一个重要的机制那就是我们在spring team里整合"reactive"以及非阻塞式背压机制.在同步里,命令式的代码,阻塞式地调用服务为普通的表单充当背压机制强迫调用者等待.在非阻塞式编程中,控制事件的频率就变得很重要防止快速的生产者不会压垮他的目的地.


        Reactive Streams 是一个定义了使用背压机制的异步组件之间交互设计的小型说明书(在Java9中也采纳了).例如,一个数据仓库(可以看做Publisher)可以生产数据,然后HTTP Server(看做订阅者)可以写入到响应里.Reactive Streams的主要目的是让订阅者可以控制生产者产生数据的速度有多快或有多慢.
        这里有个问题,如果生产者不能降速怎么办?我们要明白,Reactive Stream的目的只是简历一个机制和边界.如果生产者不能降速,就必须决定是否要开启缓冲,放弃或失败.

1.1.2 Reactive API

        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获取更多地细节.

1.1.3 Programming Models

        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(这个词不知道怎么翻译合适).

1.1.4 适用性

        Spring MVC还是 WebFlux?

        一个很自然的问题要问,但要建立一个不完全二分法.实际上,扩展可用范围的情况下两者是可以一起工作的.这两者被设计得彼此之间是有一定的连续性和一致性的,他们可以被并列使用,一方可以从另一方中获益和反馈.以下的图显示了这两者的联系,他们的共性以及他们各自特有的一些支持:

spring mvc and webflux venn

我们建议你考虑以下具体的几点:

  • 如果你有一个Spring MVC应用程序运行地很好,那就不要改变它.命令式程序设计从编写,理解和调试上来说都是最简单的.从历史的角度来看,因为大多数类库都是阻塞的,所以你有最大限度的选择余地.
  • 如果你正在谋求一站式的非阻塞web技术栈,Spring WebFlux在这一方面和其他框架一样提供了执行模型的好处额停了一些非阻塞的服务器选择(Netty,Tomcat,Jetty,Undertow和Servlet3.1容器),一个程序设计模型的选择(注解Controller和函数式web endpoints)和一个响应式类库选择(Reactor,RXJava或者其他的).
  • 如果你因为使用Java8的lambda或Kotlin而对轻量级,函数式web框架感兴趣,你可以使用Spring WebFlux的函数式web endpoints.对于较小的应用程序或不太复杂的微服务来说,这也是个不错的选择,因为他们可以从更大的透明度和控制权中获益.
  • 在微服务架构中,你有一个混合了SpringMVC或Spring WebFlux controller或使用了Spring WebFlux 函数式endpoint的项目.对不同框架间基于一样注解的程序设计模型的支持时复用更加容易,同时还可以为合适的工作选择合适的工具.
  • 评估一个应用程序的一个简单方式就是检查他的依赖.如果你要使用阻塞型的持久化API(JPA,JDBC)或者网络api,Spring MVC至少对于常见架构来说是一种最好的选择.使用Reactor和RxJava在分离线程上执行一些阻塞式的调用从技术角度来讲是可行的,只是你无法充分利用非阻塞式web栈.
  • 如果你有一个使用了远程服务调用的Spring MVC应用程序,尝试一下响应式的WebClient.你可以直接从SpringMVC的Controller方法中直接返回响应式的类型(Reactor,RxJava或其他的).每次调用的延迟越大或调用之间相互依赖越大,动态效益越大.Spring MVC的controller也可以调用其他的响应式组件.
  • 如果你有一个大型的团队,请牢记在转变过程中非阻塞,函数式和声明式程序设计陡峭的学习路线.一个非全盘式转变的可实践的方式就是使用响应式的WebClient.初次之外,衡量利益小范围的开始.我们认为,应用程序的范围很宽泛的话,就没有转移的必要.如果你不太确定希望得到的益处是什么,可以通过学习非阻塞IO是如何工作的(例如,nodejs单线程的并发性)以及它的影响.

1.1.5 服务器

       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.

1.1.6 性能

       性能这个词有很多特征和含义.Reactive 和非阻塞通常不会使应用程序运行地更快.在某些场景下,他们也可以.(例如,在并行条件下使用WebClient来执行远程调用的话).整体来说,非阻塞方式可能需要做更多的工作并且他也会稍微增加请求处理的时间.

        对reactive和非阻塞好处的预期关键在于使用小,固定的线程数和更少的内存来扩展的能力.这使应用程序在加载的时候更加有弹性,因为他们以一种更可以预测的方式扩展.然而为了看到这些好处,你需要一些延迟(包括比较慢的不可预知的网络I/O).那是响应式堆栈开始显示他力量的地方,并且这些不同是非常吸引人的.

 

1.1.7 并发模型

       Spring MVC和Spring WebFlux都支持注解Controllers,但他们在并发模型和对阻塞和线程的默认呈现(assumptions)上是非常不同的.

       在Spring MVC(和通用的servlet应用)中,都假设应用程序是阻塞当前线程的(例如,远程调用),并且出于这个原因,servlet容器处理请求的期间使用一个巨大的线程池来吸收潜在的阻塞.

        在Spring WebFlux(和非阻塞服务器)中,假设应用程序是非阻塞的,所以,非阻塞服务器使用小的,固定代销的线程池(event loop workders)来处理请求.

       "弹性伸缩"和"小数量的线程"或许听起来矛盾,但是对于不会阻塞当前线程(用依赖回调来取代)意味着你不需要额外的线程,因为非阻塞调用给处理了.

调用一个阻塞API

      要是你需要使用阻塞库怎么办?Reactor和RxJava都提供了publishOn操作用一个不同的线程来继续处理.那意味着有一个简单的脱离舱口(一个可以离开非阻塞的出口).然而,请牢记,阻塞API对于并发模型来说不太合适.

易变的状态

        在Reactor和RxJava里,你通过操作符生命逻辑,在运行时在不同的阶段里,都会形成一个进行数据序列化处理的管道.这样做的一个主要好处就是把应用程序从不同的状态保护中解放了出来,因为管道中的应用代码是绝不会被同时调用的.

线程模型

在运行了一个使用Spring WebFlux的服务器上,你期望看到什么线程呢?

  • 在一个"vanilla"Spring WebFlux服务器上(例如,没有数据访问也没有其他可选的依赖),你能够看到一个服务器线程和几个其他的用来处理请求的线程(一般来说,线程的数目和CPU的核数是一样的).然而,Servlet容器在启动的时候就使用了更多的线程(例如,tomcat是10个),来支持servlet(阻塞)I/O和servlet3.1(非阻塞)I/O的用法.
  • 响应式的WebClient操作是用Event Loop方式.所以你可以看到少量的固定数量的线程和他关联.(例如,使用了Reactor Netty连接的reactor-http-nio).然而,如果Reactor Netty在客户端和服务端都被使用了,这两者之间的event loop资源默认是被共享的.
  • Reactor和RxJava提供了抽象化的线程池,调度器目的是结合publishOn操作符在不同的线程池之间切换操作.调度器有一个名字,建议这个名字是一个具体的并发策略--例如,"parallel"(因为CPU-bound使用有限的线程数来工作)或者"elastic"(因为I/O-bound使用大量的线程来工作).如果你看到这类的线程,这就意味着一些代码正在使用一个具体的使用了Scheduler策略的线程池.
  • 数据访问库和其他第三方库依赖也创建和使用了他们自己的线程.

配置

       Spring 框架不为启动和停止servers提供支持.为了给一个服务器配置一个线程模型,你需要使用指定的服务器配置APIs,或者,你使用Spring Boot,检查Spring Boot为每个服务器提供的配置选项.你可以直接配置WebClient.对于所有其他的库,可以参考他们各自的文档.

如果喜欢,可以给个好评鼓励一下,嘿嘿.稍后会发布第二篇《Reactive Core》,敬请期待

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值