阅读 291

Spring Leader分享:Spring Framework之再探Core Container 下

特别说明

这是一个由simviso团队进行的关于Spring Framework 5.2版本内容分享的视频翻译文档,分享者是Spring Framework 5.2项目leader。方便大家在未来某个时候回顾的时候可以快速定位内容。

视频地址:

【国外前沿技术分享-后端-中文字幕】Spring Framework之再探Core Container 上

【国外前沿技术分享-后端-中文字幕】Spring Framework之再探Core Container 中

【国外前沿技术分享-后端-中文字幕】Spring Framework之再探Core Container 下

视频翻译文字版权归 simviso所有,未经授权,请勿转载

参与人员名单:

1. 响应式在Spring中的应用

Ok,虽然放在最后但依然比较重要,让我们接着讨论响应式的话题。今天响应式的话题主要是围绕大家所知的Spring WebFlux。我们在Spring Framework 5.2 中引入了响应式Endpoints(请求路径)的Web Controller模型,这里跟老一套Controller的典型结构很像。

这里的@GetMapping注解声明了HTTP Endpoints(请求路径),以及响应式的返回值(Mono)。在这个特定的例子中,响应式的返回值是Reactor Mono和Reactor Flux类型。同时,返回类型也可以是Reactive Stream Publisher的任意一种实现,只不过这里的Mono和Flux是Reactor Project下的对Publisher的一种特殊实现。你也可以返回RxJava下的Flowable和Single类型,无论你选择哪种,我们都需要知道该对它们进行怎样的处理。我们在运行时将它们集成到我们的Reactive Pipeline中。通过Reactor Core这个项目来对这个过程进行Reactive适配实现,我们在Spring 5.0中就已经引入了Reactor Core。在接下来的时间里,我将阐述一些细节以及更深入的东西。

这些返回响应式返回值的方法,在Spring MVC中同样可用。Spring WebFlux 是我们响应式Web栈,在Netty上面运行的EventLoop和Web栈是完全分离的。但如果我们讨论的是Spring MVC的话,它是一个基于Servlet的Web栈。你同样可以响应式的方式返回数据,即可以在这里返回Reactive Stream Publisher。它们虽然具有同样的组件模型,但是它们运行时的处理策略是不一样的。我们基本上是将Servlet请求切换到它的异步模式,我们对Servlet的异步流处理适配了一套Reactive Stream Publisher实现,这里甚至使用了一点背压。

隐藏在它们背后的是不一样的架构处理,但是它们表达了一个类似的意图,类似的目的。我们正尽最大的努力来让Reactive Stream Publisher可以应用在Spring MVC Servlet 架构上面。你可以很好的基于一些需求来进行工作 而这些需求不需要你进行完全的背压或完全的基于EventLoop的处理(Netty中的EventLoop处理)。它是基于一个DeferredResult来替代实现。

Spring MVC距离拥有这样功能的一个DeferredResult还有很长时间,这个就像拥有对Reactive Stream Publisher这样的返回值类型进行处理能力的DeferredResult。如果你在WebFlux上进行同种类型的请求处理,它们实际上通过基于EventLoop的架构全程进行响应式处理。从此处可以看出,在WebFlux上对于请求路径的处理(Endpoints)是有点不一样的。在Spring MVC中你可以将常规标准的阻塞式同步Endpoints(图中路径对应的处理方法)与Reactive Streams Publisher这种返回类型混合使用。想要做到这点真的还有很长的路要走。

2. 响应式事务处理

和刚才所讲的这些事情一样,大部分的Spring功能都可以通过一些有趣的途径将它们整合到一起。对于响应式功能来讲,同样如此。一个很明显的例子就是Spring Framework 5.2中的响应式事务处理。我们拿图中这个Service class中的@Transactional方法来讲。在方法上声明@Transactional注解,同时返回一个Reactive Streams Publisher。当我们进行事务拦截的时候会说,这不是一个常规的事务方法常规的事务方法是在同一个线程上我们在调用方法时开启事务,在方法返回的时候对事务进行 commit 或 rollback。

这是一个不一样的method,它需要通过一个指令级流式编程的风格返回一个Reactive Streams Publisher。你往往需要在里面通过一系列方法建立管道式的链式调用。在这里,它调用了一个Repositiory的指令,可能是一个Spring Data Repositiory,也可能是自定义的,这里并没有什么区别。它调用一个用于返回一个Mono或者Flux的repository。所以我们仅仅通过该repository 就可以得到想要的。

当然你可以在这里通过响应式链式操作来做任何事。如果你需要,你可以对元素进行后置处理,此时事务会很智能的切换到一种不一样的操作模式下,在这个事务操作背后,会有一个承载响应式链式执行事务信息的context 在操作调用之初就已经默默在背后运行了(译者注:底层是基于Spring Reactor的Context实现的)。这个repository操作在执行时会检测事务的context,然后在事务上下文中工作。在响应式链式操作执行完毕后跳出事务操作。但是要注意的一点,事务会在会在EventLoop中触发。这里,事务并不是在调用该方法得到返回值时执行,而是在 pipeline(响应式链式)实际执行时,我们才选择是提交还是回滚(译者注:Context是基于订阅者的,只有发生订阅,才会有Context产生)。显而易见,Repository 实现需要有感知Transaction Context的特性,那就需要Repository 有专门的事务实现支持。

而且和标准的事务一样,你也需要有一个这样的Repository(比如基于JPA的实现,基于Hibernate的实现,基于JDBC的实现)可以很好的和事务一起使用,只不过限制有点多。我们目前支持基于R2DBC的Repositories,它是我们响应式关系型数据库连接的抽象实现。

R2DBC是Pivotal赞助的一个独立项目,同时我们整合了大多数常见数据库的驱动实现,包括MongoDB。当发生数据存储的时候我们有一个事务上下文模型,当然我们也支持索引排列,这就是我们在Spring Framework 5.2 中实现响应式事务支持所选择的方式。

关于事务背后的机制就到这里,我们也明确的提及了R2DBC和MongoDB,它们背后的实现也不一样。它们是不同的SPI。如果你对整合这种响应式处理比较感兴趣,比如一些响应式的数据存储API。基于此你的主要精力应该放在ReactiveTransactionManager SPI上。 这是一个很不一样的SPI。当然,也是因为这里它并不基于Threadlocal工作。它们在Spring MVC和Spring WebFlux中唯一有点像的地方在于它们都使用一样的注解。这是两种SPI,PlatformTransactionManager 是标准的基于Threadlocal绑定的事务处理机制。ReactiveTransactionManager则是基于响应式链式操作绑定的事务机制,使用了一样的注解配置来安装。

3. Reactive Messaging与RSocket

另外一个值得一提的话题就是在Spring 5.2中才有的Reactive Messaging。Reactive Messaging使用起来非常简单。如果你有WebFlux的使用经验的话,这里它就和使用Endpoints是一样的,但是需要集成Spring Messaging模块。这样就可以通过上面的@MessageMapping注解并返回Reactive Stream Publisher来做到对Reactive Messaging的使用。关于这个消息集成模块,当然它可以和JMS一样,可以基于同步。在你使用这个消息技术时,你真正依赖的是底层的消息架构,我们这里主要是基于RSocket这个响应式流处理模型。

RSocket也是一个行业合作所诞生的产物,不仅仅是由Netifi(Rscoket公司)来为Rscoket提供基础开发(译者注:Spring旗下的Reactor-Netty是它的核心基础库)。所以RSocket是一个比较有趣的通信模型,它可以通过响应式Endpoints(译者注:将请求路径和对应的处理类一起封装的一个类),来直接映射到RSocket的基础设施API,这是我们为什么推荐使用它。我们打算自上而下进行响应式的支持。拿数据存储来讲,你可以使用支持响应式的Repository来进行数据存储操作。我们想将响应式操作贯穿整个Spring操作栈,包括messaging endpoints 和web endpoints。这也是我们的主要目标,对于此处的消息传递同样如此。整个过程基于一个像Rsocket一样的消息架构进行,即关于消息绑定和驱动都是基于响应式流的能力。然后你可以直接通过Spring 5.2 中的messaging endpoint来进行使用。

4. Reactive Application Event

在响应式介绍的最后,除了常见的针对请求的响应式编程处理,还有一个很棒的地方就是关于Application Events。关于ApplicationEvent这个基础类在Spring中已经存在很久了。传统的,事件监听器在很多情况下都是同步执行的,并不会阻塞操作。但是如果对ApplicationEventMulticaster进行线程池配置(调用setTaskExecutor设置),那就有可能在调用时产生阻塞。有很多事情依然可以通过ApplicationEvent来做到,但是有一件事做不到。就是对那些以返回值响应式类型的eventlistener方法实现的处理(看图中的@EventListener注解方法)。那我们可以这样说,这里有一个事件,对应这个事件会有一个响应式的链式调用管道,你应该将它们集成到你的事件广播中,这也是我们在Spring 5.2中正努力做到的事情。它还没有完全可用,即你可以使用一个Mono类型或CompletableFuture类型的返回值来表达说。我是一个EventListener,我可以做到异步或响应式链式执行。当Multicaster看到这个事件信号时,就会根据条件判断选择这个最合适的EventListener对事件进行处理。所以关于Application Events这块儿其实是Spring整个响应式迭代过程中的最后一块拼图。主要也是由于它在架构中经常用到,那如果使用响应式开发的话也不会例外。

你可以看到,我前面提到的这些代码(如Mono、Flux)都是基于Spring Reactor 的API。你也可以使用Rxjava的Single和Flowable API。在很多情况下,你也可以用CompletableFuture,但没有背压支持,就是一个单个元素,但是它也可以被适配转换到响应式链式处理中,并运行在Netty的一个eventloop中。我们底层有一套可以将它自动转换为响应式API类型的实现。而且这套实现已经存在很久了,可以很好的处理RxJava 和JDK 9 下的java.util.concurrent.Flow API类型或其他响应式publisher实现。

我们即将在Spring 5.2中加入全新的Kotlin协程支持,也是基于响应式API来适配的。在Kotlin中,协程是Kotlin语言的基本功能,作为一种可挂起的方法。与之伴随的是Kotlin的底层API —— CoroutineContext。CoroutineContext实际上非常接近响应式模型。我们在Spring Framework 5.2中将CoroutineContext直接适配到我们的响应式架构中。因此,你也可以选择基于Reactor或响应式的API来使用Kotlin的协程。同时我们自动的将它们适配到我们的响应式请求路径处理(Endpoint)中,底层做了一些API适配和一些Kotlin扩展。在Spring Framework 5.2版本中我们将加入Kotlin协程支持。

so,我基本上已经讲完了我想说的。谈到这里,我希望前面的内容多少能引起你的兴趣。这也是我们去年谈过的一些内容,我们在响应式架构上做了很多的改进。依靠Java 8 API进行更全面更广泛的改进,使用了Java 8语言的特性,并对注解处理进行了完全重新实现。特别是在性能调优、启动性能调优、热点性能调优等方面,通过这些改进允许我们为你提供一个更好的性能体验。基本上Spring Framework 5.2 RC1版本会在七月份出来。GA版本计划在九月上旬发布,在此之后会发布SpringBoot 2.2的新版本。OK,这就是我今天想要带给你们的。感谢各位的参与,如果各位有任何疑问,在演讲结束后我很乐意为各位解答。再次感谢并希望你们能享受接下来的show。

更多请关注我们的公众号: