今天和大家聊聊为什么我认为Spring仍然会是云原生时代优秀平台之一。
时间回到2015年,当时我在华盛顿参与SpringOne 2015大会,主题演讲是Cloud Native Enterprise。那次大会的口号也是Cloud Native,铺天盖地的海报都与Cloud Native有关。
你可能会感到奇怪,那个时候容器还没有流行啊,怎么就敢称为Cloud Native呢?虽然不少同学对Cloud Native的理解可能不太一样,但是越来越多的人相信“Cloud native is about culture, not containers”。可以肯定的是,云原生不等于容器,它是一种软件开发的文化,即便后续有基于V8隔离的Serverless或者WebAssembly FaaS平台,也还是云原生的范畴。
而在云原生的潮流下,Spring做了很多事情,比如支持分布式架构的Spring Cloud,它基本是Java应用分布式和云化的必备框架,各个云厂商都有Spring Cloud的对接版本,比如Spring Cloud Alibaba、AWS、Azure、GCP等,Spring Cloud极大地简化了Java应用和云服务的对接。
可以说,在微服务逐渐流行的过程中,Spring生态一直处于领先地位。很多人可能会疑惑:Spring之前取得的这些成就我们都知道,可这两年好像并没有什么发展,Spring还能继续引领该技术趋势吗?
其实,之所以会有这样的疑惑,是因为当我们谈到基于云和云原生环境的开发时,普遍更关注的是技术栈的选择,而这背后主要是费用的问题。
每一位开发者或中小公司都希望购买更少的云资源干更多的事情,其中最主要的是内存和CPU。内存消耗要求小,如果能编译为独立可执行程序,就尽量选择一些轻量型的开发框架;而且要考虑语言高效且全异步化的框架,这样可以保证充分利用CPU,避免线程等待等造成的浪费。
综合来看,很多人会更倾向于选择Rust、Node.js、Golang等技术栈。而Java启动慢,消耗内存多,好像还比较费钱,看起来已经不太适合接下来的云原生时代。
那么作为基于Java语言的Spring生态,还能否适应新的开发方式,比如Cloud Native、Serverless、Faas等,它还会是云原生时代的最佳平台的选择吗?
接下来,我将从5个角度来为你分析一下这个问题,分别是:Java和JDK的发展、充满良性竞争的JVM语言、成熟的面向服务架构的Spring Boot和Spring Cloud、让事件驱动架构更易使用的Spring Reactive。
一、Java和JDK的发展
我们知道Spring基于的是Java框架,而JDK又是运行Java程序的基础。要探讨“Spring还会不会是云原生的最佳平台之一”这个问题,我们需要先来看看JDK这个底座怎么样,是不是能为Spring与时俱进的发展提供良好的基础。
首先是版本迭代。现在OpenJDK的开发速度非常快,之前是三年出一个重大版本,现在半年就出一个版本。好多同学感叹现在都快Java 17了,而自己还在用Java 1.8。我们都说软件开发要小步快跑,这个是一个好的开发方式:小步快跑、快速反馈、快速迭代开发。类似的还有JavaScript一年一版,TypeScript一年三版,Java一年两版,可以说都是非常好的节奏。
其次是JDK特性的更新。如果你关注JDK的话就不难发现,越来越多的特性被融入到JDK中,比如类似协程的轻量级线程Loom项目、提升Native调用支持的Panama项目(SIMD支持)、更高级的GC算法等。
而针对我们前面提到的Java启动慢、消耗内存多的问题,Oracle推出了基于OpenJDK的GraalVM,它可以直接在JVM中运行JavaScript、Python、Ruby等语言,是名副其实的Ployglot JVM。另外,GraalVM还提供了Native Image特性,可以将Java代码转换为独立可执行程序。Spring也推出Spring Native项目,可以非常方便将Spring Boot应用native-image化,这样Spring应用启动的速度更快,消耗的内存也更少。native-image化后的Spring应用就可以满足每一位开发者或中小公司对“上云且费用更少”的需求。即便是Java的命令行应用,GraalVM也有非常好的解决方案——Picocli + GraalVM Native Image + upx,它可以将Java应用编译为更便捷的可执行程序。
下图就是GraalVM多语言和Native Image支持列表,如下:
此外,各个云厂商也在积极JDK的开发,如AliJDK、Amazon Corretto、Azul等,这些都是对JDK发展有非常好的作用。
所以说,对Spring而言,JDK这个基础还是非常稳固的。
二、充满良性竞争的 JVM 语言
JDK并不是只能支持Java这一门编程语言,相反,它可以支持多种,比如Kotlin、Scala、Groovy等,这里我们统一称之为JVM语言。当下,JVM语言正处于一个充满良性的竞争状态。
我们知道每种语言都有自己擅长的使用场景。在云原生时代,多语言开发支持非常重要,开发人员需要根据实际的业务场景选择不同的开发语言。无论JVM语言或者GraalVM支持的语言,都可以和Spring直接整合,方便开发人员充分利用Spring技术栈做开发。
其中,对Spring支持最好的语言是Kotlin。Kotlin和Spring的整合已经渗透到框架的各个方面,比如Spring Boot和Kotlin整合,Spring Webflux对Kotlin Coroutines & Flow的支持,Spring Data、SpringIntegration、SpringCloud Function等也都包含对Kotlin的支持,这些都实现了对Kotlin的深度整合,目的就是让语言和框架结合得更好,提升开发效率。此外,Kotlin和Scala也是与Spring整合得比较好的语言。
因为Java、Kotlin和Scala这三种语言在Spring中的整合非常好,所以接下来我就对它们具体说明一下,帮助你对JVM语言当前的发展情况有一个比较清晰的认知。
Java
Java语言的发展还是非常快速的,伴随着OpenJDK的开发,Java半年就会推出一个版本,每个版本都会伴随着语法的升级、标准库的升级等,极大地方便了开发人员。从Java 9到16,已经完成了很多语法和特性方面的提升,比如大家知道var关键字(局部变量类型推导)、Text block、Pattern Matching for instanceof、Record、sealed interface等。
Kotlin
虽然说Kotlin不完全是针对JVM的,它也包括Native、JavaScript等,但是Kotlin对JVM和Android的支持,目前是众多Kotlin平台中特性最多且稳定的。
现在Kotlin的发展势头非常迅速,已经孵化了多个子项目,比如最新的Kotlin Multiplatform Mobile(一套代码支持多个手机平台)、 Kotlin Compose for Desktop(一套代码开发多操作系统的桌面端应用)等。当然,这些变化中最引入注目的是Kotlin 1.5推出的Kotlin intermediate representation (IR) ,借助于新的IR,Kotlin的代码可以编译输出到各种目标平台且程序更优,如大家都关注的Kotlin WebAssembly。
Scala
Scala 2是2006年3月发布的,距今已经有非常长的时间了。好的消息是Scala 3.0已经月2021年5月发布啦。Scala 3在添加新特性的同时,去除了Scala 2中一些晦涩的特性,目的是让Scala更好使用。如果你有兴趣,可以关注下Scala3的官网。
Scala 3还有一个核心的变化是编译器的调整,名称为TASTy,是 Typed Abstract Syntax Trees的缩写,结构如下:
借助于TASTy,目前已经有Scala.js ,后续有Scala WebAssembly也不奇怪。
Kotlin和Scala对函数编程都非常友好,在这点上都弥补了Java的不足。此外,Kotlin和Scala同时支持JavaScript编译输出,这个对基于V8的Serverless平台帮助非常大,比如Cloudflare的Serverless平台。此外,Kotlin和Scala也在积极探索对WebAssembly支持,这些都是支持Web开发的好消息。
当然,JVM语言还有很多,如Groovy、Clojure等,也都在积极发展,这些JVM语言彼此竞争,彼此借鉴,这对开发者来说,都是非常好的消息。
Spring依赖的JDK和JVM语言说完了,下面我们来聊聊Spring中非常成功的两个项目SpringBoot和Spring Cloud。
Spring Boot和Spring Cloud是非常成功的两个项目,基于Java技术栈的互联网公司基本都在使用。此外,近期如果你有关注Spring顶级布道师Josh Long的演讲的话,就会发现他一直在讲Spring Reactive,他还编写了一本书,就叫《Spring Reactive》。
那为什么Spring Boot、Spring Cloud和Spring Reactive会如此备受关注呢?
我们知道,对于云原生来讲,有两种非常重要的架构方案,一个是面向服务化架构,另一个是事件驱动架构。面向服务化架构通常基于同步的请求/响应模型,Spring Boot、Spring Cloud的目标就是支持这一特性;事件驱动架构则基于异步化的消息模型和消息路由,而Spring Reactive这个项目的核心就是支撑这一架构。
接下来,我就带你看看Spring具体是如何支持云原生里这两种重要的架构方案的,借此,你也会对Spring里重要的三个项目Spring Boot、Spring Cloud和Spring Reactive有一个比较清楚的认知。
三、成熟的面向服务架构的Spring Boot和Spring Cloud
通常,云原生下的应用都倾向于微服务设计,而微服务设计的核心内容就是面向服务化架构设计和应用编程接口(API)管理,Spring Boot和Spring Cloud的目标就是支持这一特性的,而且这两个项目做得非常成功,基于Java技术栈的互联网公司基本都在使用。
我们知道,服务化接口最典型的结构就是请求(Request)/响应(Response)模式,尤其是Web的Request/Response模式。当然了,远程RPC的request/response也在此范畴,比如HTTP REST、gRPC和Dubbo。
对应到Spring中面向服务架构设计,SpringMVC可以非常好地支持Web和HTTP REST API,还包括对OpenAPI的支持。其他RPC类的通讯,也都有对应的Spring Boot starter支持,在Spring Boot中也有对应的starter组件支持,开发也非常便捷。如果要暴露其他形式服务,如Web Service和SOAP等传统服务形式,也可以通过Spring Web Services(spring-ws)项目达成。
另一个特性API管理,主要涉及服务治理和服务消费端调用,对应的核心技术栈主要是Spring Cloud项目。Spring Cloud为服务化架构和API管理提供了多种基础设置,如服务注册发现、复杂均衡、API Gateway、断路保护等,这些技术栈方便我们更好地管理和治理服务,同时也让消费端能更好地调用服务。
综合下来,服务化架构和API管理,对应到Spring Boot和Spring Cloud技术栈,典型的架构如下:
如果你的架构是面向服务的,如涉及到服务编排、服务治理和API Gateway等,那么Spring Cloud会是非常好的选择,特性完备、框架稳定、文档完善、历经过诸如阿里巴巴等亲自实践。如果你的公司基于Java技术栈,那么Spring Cloud是实现微服务架构最好的支持和实践。
阿里巴巴从2018年7月开始就开源了Spring Cloud Alibaba,截止至今,它已经获得了超过1.9万的star数,已经超过了所有其他实现的总和。从X-lab 开放实验室发布的《2020年微服务领域开源数字化报告》中,我们也看到Spring Cloud Alibaba已经成为最活跃、最受开发者欢迎、工具链最完善的Spring Cloud实现。大家在选型过程中可以考虑。
四、让事件驱动架构更易使用的Spring Reactive
前面我们讲述了Spring对面向服务架构的支持,还有另外一种架构模式,就是事件驱动架构。事件驱动架构被普遍认为是云原生首选的架构方案,那么Spring对这方面支持如何?这就涉及到Spring Reactive这个项目了,其核心就是支撑事件驱动架构。
Spring Reactive是面向异步化消息的,消息或者事件是企业集成模式(EIP)首推的架构模式,你可能听说过Spring企业集成(SpringIntegration)这个项目,其主要就是负责企业集成。Spring Reactive的目的则是让事件驱动架构设计也能成为应用架构的首选,而且要更加简单。
这里讲一个题外话:我和一些资深的Spring Boot和Spring Cloud程序员交流过,大家对各种服务化接口设计如数家珍,甚至包括代码层级的细节,但是一讨论到企业集成模式(EIP),都表示这是Martin Fowler的知名大作,问及大家的系统中是否使用到诸如Apache Camel或者Spring Integration,大家都表示现在面向服务结构已经满足业务需求啦。
那么这里就有一个问题,如此知名的EIP、ESB或者EDA为何没有在架构设计中所采纳?是灯下黑被遗忘、还是门槛太高、或者压根就是一个纯理论,完全脱离实践?
在面向服务化的架构设计中,基本都是基于服务接口调用,并且调用是同步的,所以服务编排、API管理起着非常重要的作用。当然在面向服务化的架构设计中,我们也会引入一些消息驱动设计,借助像Kafka或者其他MQ系统,处理一些异步化或者数据分析的场景,如用户注册后发一封邮件、用户登录触发事件进行安全审计、商品信息或价格修改后触发搜索引擎增量索引、用户下单后触发各种通知等等。
面向服务有其好处,但是要解决非常多的问题,如非阻塞、服务注册和发现、高性能、易集成等,这也是很多专家提出云原生场景中,事件驱动架构才是主流。
事件驱动架构不涉及服务注册和发现这些负责的机制,基于消息的路由也非常容易,异步化消息通讯的性能更高(当然你的账单也更小),各种SaaS服务集成也更简单(EventBridge产品)、FaaS触发容易等等。如果你想了解更多,可以看看《Building Event-Driven Microservices》这本书。这里贴一下Redhat推荐的基于微服务思想的事件驱动架构设计,如下:
从这张图中我们可以看出,所有系统之间的交互都是通过消息进行通讯的,微服务不再是面向接口的请求/响应设计,处理来自不同的应用的Request/Response请求,而是以一种异步化的方式不断地在处理来自Broker的消息。
说到这里,你可能明白了,如果说Cloud Native以事件驱动设计架构为核心,那么Spring Reactive的目的是让事件驱动架构简单易用,开发便捷,这个就是Spring Boot和Cloud对面向服务架构的支持是一样的。此外这两种架构并不是矛盾的,都是可以相互共存和相互补充的。
要实现更好的事件驱动架构,两个基础少不了:异步化和消息化。异步化可以解决线程等待的问题,而消息及其消息路由,可以很好地实现应用的松耦合。当前越来越多的产品使用异步化消息方案,各种消息产品中间件自不必说,大家熟知的HTTP/2都是基于异步消息通讯的。
那为何要异步化?我们知道消息处理的模型和同步服务调用是不一样的,如消息处理的Event Loop和Actor模型,都是非常高效的消息处理方式,如果我们在消息处理的过程中,还要处理基于线程池的同步服务调用,势必会影响模型和性能。当然也不是完全不能有同步,只是尽量避免过多的服务同步调用的场景。
围绕异步化和消息的场景,Spring要做非常多的工作,我们都知道Java对异步化支持非常滞后。Java 1.8加入了CompletionStage接口,Java 9添加了Reactive Stream支持介入了Flow。大家一直关注的轻量级线程的Loom项目还在开发中,预计要到Java 17才能发布。
所以目前Java多数的项目项目都会选择Reactive框架,当然Spring团队开发了Reactor框架,增加了Reactor对Netty、Kafka等适配,从而保证从底层开始就是异步化,所以这也是起名为Spring Reactive的原因。
接下来就是大家都看到的场景,Spring Webflux、Spring Integration、Spring Data等众多项目都添加了对异步化的支持,所有的这些调整,就是要确保从底层开始就是全异步化的,你可以理解为5G的非独立组网和独立组网两种方式,只是Spring选择了Reactive这种独立组网、独立生态的方式,当然工作量也是非常大的。这其中就涉及了两个非常大的问题:数据库和分布式通讯。
目前大多数NoSQL产品都提供了异步化的接口,异步化访问NoSQL产品没有什么问题。虽然NoSQL发展很迅猛,但是还是没有撼动数据库的地位,我们都知道Java中数据库访问的规范是JDBC,但是JDBC是同步的接口,所以Spring启动了R2DBC项目,目的就是能够以Reactive异步化接口访问数据库,目前来说,在Spring框架下使用JDBC和R2DBC的体验几乎是一致的。
这里我和同学们同步两个消息:Spring 5.3已经内置R2DBC支持,也就是spring-r2dbc,和spring-jdbc并列。大家都喜欢使用的MySQL数据库,也推出了mariadb-r2dbc 1.0.0稳定版,Oracle也推出了R2DBC版本的驱动。当然Java中的DB框架,Spring JPA、MyBatis等都添加了对R2DBC的适配。Hibernate也推出了hibernate-reactive项目(基于Vert.x database),虽然是不是基于R2DBC的,但是也是全异步化的。
另外一个问题就是分布式通讯,这个在微服务架构中扮演中非常重要的作用。一直以来Spring主要支持HTTP,但是HTTP协议主要是针对浏览器设计的,在后端服务之间通讯优势并不明显,而且性能上也没有优势。当然基于HTTP/2异步消息通讯的gRPC是一个很好的选择,但是Spring一直没有内置gRPC支持,当然这对大多数开发者来说,这不是什么大问题,第三方的grpc-spring-boot-starter做的也非常好。
如果你关注Spring发布的话应该知道,Spring 5.2后续版本选择RSocket通讯协议,并将RSocket内置到spring-message项目中。为何?RSocket是全异步的二进制消息通讯协议,提供了完备的通讯模型,而且是对等通讯的,非常匹配事件驱动架构,这也是Spring Reactive将RSocket纳入到Spring核心的原因,当然RSocket背后的开发,Spring和阿里团队起着主导作用。
CNCF下有一个CloudEvents规范,主要解决异构系统的事件通讯,消息和事件在数据结构上是一致的,都是Headers和Payload(data)结构,同时包含消息头的扩展。最新的cloudevents-java SDK 2.1,Spring messaging添加了对CloudEvents的支持,同时支持CloudEvent接口的编解码支持,将消息和事件进行了统一和整合,在Spring中处理CloudEvents和消息更简单。
和企业集成相关产品,如Apache Camel,基于Akka的Alpakk、MuleSoft的Mule 4、Spring Integration等,这些产品都提供了对Reactive的集成,可以说可以和Spring Reactive无缝对接。
五、总结
回到文章的问题,为什么Spring仍然会是云原生时代最佳平台之一?总结下来,Java和JDK的开发处以非常好的发展,JVM语言良性发展且充满竞争。成熟的Spring Boot和Spring Cloud已经让面向服务的架构设计简单易用,而Spring Reactive则将事件驱动架构更易被使用,同时可以让更多的企业系统完成和Spring Reacitve对接,无论是IoT设备、ESB产品、SaaS或者云服务等。
针对微服务的场景,Spring Cloud提供了成熟的面向架构服务基础,Spring Reactive则是面向未来的事件驱动架构,当然这里并不是说,面向服务的架构已经过时,事实上面向服务的架构非常成功,但是在云原生或新的Serverless环境下,可能事件驱动架构可能更合理一些,或者是两者的配合使用,不用担心,Spring在这两方面都做的非常好。