Java架构-如何设计实现真正的响应式微服务系统?

引言
这是一篇讲解微服务系统在扩展性伸缩性方面的演进文章,Jonas Boner认为目前普通的微服务最终将演进为事件驱动的响应式微系统架构 。
今天系统架构大概有三种:单体Monolith、微单体Micoliths和微服务Microsystems,所谓微单体就是介于单体和微服务之间的一种,有很多服务,数据库也进行了分库分表,不像单体那样只有一个大的WAR应用和一个数据库,但是又不像微服务那样,每个服务都有自己独立的数据库,互不干涉 。
大部分微单体系统是切分了单体系统而形成的,每个Microlith由一个台服务器运行,有自己的REST/Servlet,有自己的服务和JPA数据库,数据库访问是一种同步堵塞式的数据库线程连接池方式访问,微单之间式通过同步的堵塞的RPC访问(比如Dubbo和gRPC),这样的系统没有弹性,不具有伸缩 。
Carl Hewitt说: 一个Actor模型不是真正的Actor,一个系统中应该有多个Actors 。
迈向可扩展的微服务系统是引入Actor模型,有三种有用的方式:
1. 事件第一的DDD Events-first DDD
2. 响应式设计 Reactive Design
3. 基于事件的持久 Event-Based Persistence
实践的事件第一的领域驱动设计
这种方式有别于传统的DDD,传统DDD最后可能形成一个臃肿的肥胖的聚合根,大家都把东西塞在里面,这种方式的问题在于我们过于关注聚焦名词:领域对象,就像我们过于关注数据表一样 。
相反,我们应该首先聚焦关注发生了什么事情,也就是动词,是事件 。
Greg Young说:当你开始对事件建模时,将会强迫你思考系统的行为,而不是思考系统的结构 。
具体怎么做呢?在传统DDD中,分析建模第一步是找出情景场景,也就是有界的上下文,但是如何定义有界的上下文却很难定义,有可能来自于用例需求的分解等等,但是这些边界划分不一定是面向领域的,可能是面向功能的,所以,事件建模就是通过事件来定义有界上下文 。
比如订单发生的地方是一个上下文,而进出库事件发生的地方又是一个上下文 。这些上下文都是因为发生不同事件而自然形成边界 。
事件重要特征是它代表发生的事实,这在很多跟踪系统中有非常重要的应用,比如货物跟踪,财务和人事跟踪记账系统等等 。
寻找发生的事实,如同大侦探波罗探案一样,他在东方快车谋杀案中自诩和上帝一样能看清真相,当然这也可能来自于他的强迫症,当他发现犯罪事实竟然是来自每个人的人性之恶时,他再也不敢自诩上帝了 。
我们使用DDD分析的系统真相,当然不会涉及到道德和人性幽暗,所以,在这样的逻辑系统我们是可以
扮演上帝的,那么如何发现需求中的事实真相呢?通过事件风暴,Event Storming 。
事实是遵循因果一致性的,理解事实是如何进行因果相关,Peter Alvaro说:因果性通往时空的途径 。哲学大师康德认为时空是我们观察世界的方式,而时空蕴含的因果性正是我们科学追寻发现的目标 。
因果关系代表了一种因果一致性,有因必有果,这是一致的,不会前后矛盾,是前后顺序的,如同老子说:道生一,一生二,二生三,三生万物,这也是表达了一种因果一致性 。因果一致性对立面就是矛盾,如果发现矛盾,实际就是否定了因果一致性,有时我们会从矛盾方面去证伪因果一致性 。因果一致性是形式逻辑的重要特征 。
既然存在因果一致性,就会存在一致性自然形成的边界,比如从第一因一直到最后一个果,这就自然形成了一条因果链,这条因果链自然也就形成自己的边界,如果两条因果链放在一起,我们能够区分它们也是因为边界不同 。但是在佛教或现代艺术中,经常把最后一个果和第一个因再链接起来,形成了因果循环,也就是轮回,这实际破坏了一致性,是非时空理性思维方式 。
在软件需求中,我们如何发现因果链形成的边界?一般这样边界中需要包含可变状态和已经发生的事实,这些事实其实是造成状态可变的原因,只不过表现形式不同而已 。
所以,DDD中的寻找聚合,实际就是寻找因果一致性单元,也是寻找会发生failure的单元 。

Java架构-如何设计实现真正的响应式微服务系统?

文章插图
 
响应式设计
响应式设计分为两个阶段:初期的响应式编程和分布式的响应式系统 。
响应式编程能够帮助我们让独立的实例系统(一个VM 一个JVM或一台服务器)内部运行更高效 。通向响应式第一步是引入异步,也就是引入非堵塞,能够更有效地利用资源,防止数据库连接池的暴障拖垮数据库,或一个很轻的小动作就能让巍峨的Oracle当机趴下,这些都是同步堵塞造成的罪行 。


推荐阅读