Spring WebFlux(译)

前言

今天在做幂等性校验由Spring WebMvc 转 Spring webFlux,发现网上关于 Spring webFlux 中文资料比较少,所以自己手动有道翻译一份,仅做自己学习使用,做个记录,感谢!

关于 Spring WebFlux 的翻译时基于版本 Version 5.3.6 的;

原文地址

1.Spring WebFlux

Spring框架中包含的原始web框架,Spring web MVC,是专门为Servlet API和Servlet容器而构建的。响应式堆栈web框架Spring WebFlux是在5.0版本中添加的。它是完全无阻塞的,支持响应流回压,并运行在Netty、Undertow和Servlet 3.1+容器等服务器上

这两个web框架都反映了它们的源模块的名称(Spring -webmvc和Spring -webflux),并在Spring框架中共存。每个模块都是可选的。应用程序可以使用一个或另一个模块,或者在某些情况下同时使用两个模块——例如,带有响应性WebClient的Spring MVC控制器。

1.1. Overview (概述)

为什么要创建Spring WebFlux ?

部分原因是需要一个非阻塞的web堆栈来处理少量线程的并发性,并使用更少的硬件资源进行扩展。Servlet 3.1确实为非阻塞I/O提供了一个API。然而,使用它会远离Servlet API的其他部分,其中契约是同步的(Filter, Servlet)或阻塞的(getParameter, getPart)。这是一个新的通用API作为任何非阻塞运行时的基础的动机。这很重要,因为服务器(如Netty)是在异步、非阻塞空间中建立的。

答案的另一部分是函数式编程。就像Java 5中添加注释创造了机会(比如带注释的REST控制器或单元测试)一样,Java 8中添加lambda表达式也为Java中的功能性api创造了机会。这对于允许声明式组合异步逻辑的非阻塞应用程序和延续风格的api(由CompletableFuture和ReactiveX普及)来说是一个福音。在编程模型级别,Java 8使Spring WebFlux能够提供功能性的web端点和带注释的控制器。

1.1.1. Define “Reactive” (响应式的定义)

我们谈到了“非阻塞”和“功能性”,但是响应式是什么意思呢?

术语“响应式”指的是围绕对变化作出反应而构建的编程模型——网络组件对I/O事件作出反应,UI控制器对鼠标事件作出反应,等等。从这个意义上说,非阻塞是响应式的,因为我们现在处于操作完成或数据可用时响应通知的模式,而不是被阻塞;

还有另一个重要的机制,我们在Spring团队中与“响应式”联系在一起,那就是无阻塞的背压。在同步的命令式代码中,阻塞调用是迫使调用者等待的一种自然形式的背压。在非阻塞代码中,控制事件的速率使快速的生产者不会淹没其目标变得很重要。

响应流是一个小规范(也在Java 9中采用),它定义了带有背压的异步组件之间的交互。例如,数据存储库(充当发布服务器)可以生成HTTP服务器(充当订阅服务器)随后可以写入响应的数据。响应流的主要目的是让订阅者控制发布者产生数据的速度。

思考:如果生产者不能放慢速度怎么办?
响应式流的目的只是建立机制和边界。如果生产者不能放慢速度,它就必须决定是缓冲、删除还是失败。

1.1.2. Reactive API(响应式API)

响应式流在互操作性中扮演着重要的角色。它对库和基础设施组件很感兴趣,但作为应用程序API用处不大,因为它太低级了。应用程序需要一个更高级、更丰富的功能性API来组成异步逻辑——类似于Java 8 Stream API,但不只是用于集合。这就是响应式库所扮演的角色。

Reactor是Spring WebFlux的响应式库的选择。它提供Mono和Flux API类型来处理0..1的数据序列(Mono)和0 . .N (Flux)到一组与操作符的ReactiveX词汇表对齐的丰富操作符。Reactor是一个Reactive Streams库,因此,它的所有操作人员都支持无阻塞的背压。Reactor非常关注服务器端Java。它是与Spring紧密协作开发的。

WebFlux需要Reactor作为一个核心依赖,但是它可以通过 reactive Streams 与其他的 reactive 库互操作。作为一个通用规则,WebFlux API 接受一个普通的 Publisher 作为输入,在内部将其调整为一个 Reactor 类型,使用它,并返回一个Flux或Mono作为输出。因此,您可以传递任何 Publisher 作为输入,并可以对输出应用操作,但您需要调整输出以便与另一个响应式库一起使用。只要可行(例如,带注释的控制器),WebFlux就会透明地适应RxJava或其他响应性库的使用。有关更多细节,请参阅响应式库。

除了响应式api之外,WebFlux还可以与Kotlin中的协程api一起使用,后者提供了一种更命定的编程风格。下面的Kotlin代码示例将与协程api一起提供。

1.1.3. Programming Models(编程模型)

Spring -web模块包含了Spring WebFlux的响应式基础,包括HTTP抽象、响应式流适配器,支持的服务器、编解码器和一个核心的WebHandler API,类似于Servlet API,但没有阻塞协议。

在此基础上,Spring WebFlux提供了两种编程模型的选择:

  • 带注释的控制器:与Spring MVC一致,并基于Spring-web模块中的相同注释。Spring MVC和WebFlux控制器都支持响应式(Reactor和RxJava)返回类型,因此,很难将它们区分开来。一个显著的区别是,WebFlux也支持响应式@RequestBody参数。

  • 函数式端点:基于lambda的、轻量级的函数式编程模型。您可以将其视为应用程序可以用来路由和处理请求的一个小库或一组实用程序。带注释的控制器的最大区别在于,应用程序从头到尾负责请求处理,而不是通过注释声明意图并被回调。

1.1.4. Applicability(使用范围)

Spring MVC 还是 WebFlux?

这是一个很自然的问题,但却建立了一个不合理的二分法。实际上,两者共同努力扩大了可用选择的范围。这两种设计都是为了彼此的连续性和一致性,它们可以并排使用,并且来自每一方的反馈对双方都有利。下图显示了两者之间的关系,它们有什么共同之处,以及它们各自支持的独特之处:

image.png

我们建议您考虑以下几点:

  • 如果您有一个运行良好的Spring MVC应用程序,则不需要进行更改。命令式编程是编写、理解和调试代码的最简单方法。您可以选择最多的库,因为从历史上看,大多数库都是阻塞的。

  • 如果你已经在购买一个非阻塞的web堆栈,Spring WebFlux提供了与其他网站相同的执行模型优势,还提供了服务器(Netty, Tomcat, Jetty, Undertow和Servlet 3.1+容器),编程模型(带注释的控制器和功能web端点),并选择响应式库(Reactor、RxJava或其他)。

  • 如果你对使用Java 8 lambdas或Kotlin的轻量级功能web框架感兴趣,你可以使用Spring WebFlux功能web端点。对于需求不那么复杂的小型应用程序或微服务来说,这也是一个很好的选择,这些应用程序或微服务可以受益于更大的透明度和控制。

  • 在微服务体系结构中,你可以混合使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端点的应用。在两个框架中支持相同的基于注释的编程模型,可以更容易地重用知识,同时为正确的工作选择正确的工具。

  • 评估应用程序的一个简单方法是检查它的依赖关系。如果您需要使用阻塞持久性api (JPA、JDBC)或网络api,那么Spring MVC至少是通用架构的最佳选择。在技术上,用Reactor和RxJava在单独的线程上执行阻塞调用是可行的,但你不会充分利用非阻塞的web堆栈。

  • 如果你有一个Spring MVC应用程序调用远程服务,试试响应式WebClient。您可以直接从Spring MVC控制器方法返回响应类型(Reactor、RxJava或其他)。每个调用的延迟时间或调用之间的相互依赖性越大,好处就越大。Spring MVC控制器也可以调用其他响应性组件。

  • 如果您有一个大型的团队,请记住,在向非阻塞、函数式和声明式编程的转变过程中,需要经历陡峭的学习曲线。一个不需要完全切换就启动的实用方法是使用响应式WebClient。除此之外,从小事做起,衡量好处。我们预计,对于广泛的应用来说,这种转变是不必要的。如果您不确定要寻找什么好处,可以从学习非阻塞I/O如何工作(例如,单线程Node.js上的并发性)及其影响开始。

1.1.5. Servers(服务)

Spring WebFlux支持Tomcat, Jetty, Servlet 3.1+容器,以及Netty和Undertow等非Servlet运行时。所有服务器都适应于低级的公共API,这样就可以跨服务器支持高级编程模型。

Spring WebFlux没有启动或停止服务器的内置支持。然而,很容易从Spring配置和WebFlux基础设施组装一个应用程序,并使用几行代码运行它。

Spring Boot有一个WebFlux启动器,可以自动执行这些步骤。默认情况下,初学者使用Netty,但是通过改变Maven或Gradle的依赖关系,很容易切换到Tomcat、Jetty或Undertow。Spring Boot默认为Netty,因为它更广泛地用于异步、非阻塞空间,并允许客户端和服务器共享资源。

Tomcat和Jetty可以与Spring MVC和WebFlux一起使用。但是请记住,它们的使用方式是非常不同的。Spring MVC依赖于Servlet阻塞I/O,并允许应用程序在需要时直接使用Servlet API。Spring WebFlux依赖于Servlet 3.1非阻塞I/O,并使用底层适配器背后的Servlet API。不能直接使用。

对于Undertow, Spring WebFlux直接使用Undertow API,而不使用Servlet API。

1.1.6. Performance(性能)

性能具有许多特征和意义。响应式和非阻塞通常不会使应用程序运行得更快。在某些情况下,它们可以(例如,如果使用WebClient并行运行远程调用)。总的来说,非阻塞方式需要更多的工作,这可能会略微增加所需的处理时间。

响应式和非阻塞的主要预期好处是能够使用少量固定数量的线程和更少的内存进行扩展。这使得应用程序在负载下更有弹性,因为它们可以以更可预测的方式伸缩。然而,为了观察这些好处,您需要有一些延迟(包括缓慢和不可预测的网络I/O)。这就是反应性堆栈开始显示其优势的地方,差异可能是戏剧性的。

1.1.7. Concurrency Model(并发模型)

Spring MVC和Spring WebFlux都支持带注释的控制器,但是在并发模型和阻塞和线程的默认设置上有一个关键的区别。

在Spring MVC(以及一般的servlet应用程序)中,设定应用程序可以阻塞当前线程(例如,对于远程调用)。因此,servlet容器使用大型线程池来吸收请求处理期间可能出现的阻塞。

在Spring WebFlux(以及一般的非阻塞服务器)中,设定应用程序不会阻塞。因此,非阻塞服务器使用一个小的、固定大小的线程池(事件循环工作者)来处理请求。

“扩展”和“少量线程”听起来可能矛盾,但永远不阻塞当前线程(而依赖回调)意味着你不需要额外的线程,因为没有阻塞调用需要吸收。

调用阻塞API

如果您确实需要使用一个阻塞库怎么办?Reactor和RxJava都提供了publishOn操作符,以在不同的线程上继续处理。这意味着有个容易的安全出口。但是,请记住,阻塞api并不适合这种并发模型。

可变状态

在Reactor和RxJava中,通过操作符声明逻辑。在运行时,会形成一个响应式管道,其中数据会在不同的阶段顺序处理。这样做的一个关键好处是,它使应用程序不必保护可变状态,因为该管道中的应用程序代码永远不会并发调用。

线程模型

您希望在使用Spring WebFlux运行的服务器上看到哪些线程?

  • 在一个 “普通的” Spring WebFlux服务器上(例如,没有数据访问或其他可选依赖项),您可以期望一个线程用于服务器,其他几个线程用于请求处理(通常和CPU内核的数量一样多)。然而,Servlet容器可能以更多的线程(例如,Tomcat上的10个线程)启动,以支持Servlet(阻塞)I/O和Servlet 3.1(非阻塞)I/O的使用。

  • 响应式WebClient以事件循环方式操作。因此,您可以看到与此相关的少量、固定数量的处理线程(例如,Reactor-http-nio-带有Reactor Netty连接器)。但是,如果反应器Netty同时用于客户端和服务器,这两个共享事件循环资源在默认情况下。

  • Reactor和RxJava提供了线程池抽象,称为调度程序,用于publishOn操作符,该操作符用于将处理切换到不同的线程池。调度器有建议特定并发策略的名称——例如,“并行”(用于线程数量有限的cpu绑定工作)或“弹性”(用于I/ o绑定工作,用于线程数量很大)。如果您看到这样的线程,这意味着某些代码正在使用特定的线程池调度器策略。

  • 数据访问库和其他第三方依赖项也可以创建和使用它们自己的线程。

配置

Spring框架不支持启动和停止服务器。要为服务器配置线程模型,您需要使用特定于服务器的配置api,或者,如果您使用Spring Boot,请检查每个服务器的Spring Boot配置选项。您可以直接配置WebClient。对于所有其他库,请参阅它们各自的文档。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容