做者:404,转载请注明出处。欢迎关注公众号:404P。java
SOFA是蚂蚁自研的一套金融级分布式中间件,目前正在逐步向业界开源。SOFA的全称有两个,最先是Service Oriented Fabric Architecture,即面向服务的架构。随着2018年的开源,其全称改成Scalable Open Financial Architecture,便可扩展的开源金融架构。程序员
SOFA技术栈包含了微服务架构体系的各种组件,主要包括RPC框架,服务注册中心,分布式链路追踪,Metrics监控度量等。web
本文咱们来聊聊SOFA的模块化。spring
模块化在计算机领域是常常讨论的话题,在学校学编程语言的时候,教科书上说程序设计要遵循模块化原则。编程
模块化程序设计是指在进行程序设计时将一个大程序按照功能划分为若干小程序模块,每一个小程序模块完成一个肯定的功能,并在这些模块之间创建必要的联系,经过模块的互相协做完成整个功能的程序设计方法。小程序
上面这段话引自百度百科,其实精炼下就是:高内聚和低耦合。网络
最先学编程的时候,实现一个功能,全部逻辑放到一个main函数里去,后来发现理不清了,就把main里面的逻辑抽成几个函数。这是模块化吗?是。这个模块化是最基本的代码设计能力,加强代码的可读性、可维护性和可扩展性。以下图,计算成绩的时候,没有把全部逻辑放在A中,而是分在B1和B2中,单独计算,最后A调用B一、B2来实现。架构
在这种简单的程序设计中,每每更注重逻辑的内聚性,只要内聚作好了,每每就是低耦合的。框架
在真实作一些项目的时候,业务系统比较复杂,要实现的功能不少。这个时候出现了横向和纵向的模块化设计。横向的就是分层设计,纵向的就是按照不通的业务领域来设计。maven
一个业务系统,横向的模块化切分主要分为三大层:Web层,Service层,DAL层。
在业务初期,功能每每都是写在一个业务系统的,好比订单模块Order、库存模块Stock。在maven中这些模就是不一样的module,但运行都是在同一个JVM,同一个web容器中的。
这在种状况下,订单服务依赖于库存服务怎么办?订单模块的pom中引入库存模块的依赖。而后注入bean。
public class OrderService {
@Autowired
private StockService stockService;
}
复制代码
这种横纵模块化思想随着业务的复杂开始进化了。订单、库存模块虽然在一个Service层,但属于明显不一样的领域,已经被分红不一样的模块了,具备很好的内聚性,并且要引用其它模块,必须引入pom依赖以后才能访问,具备不错的隔离型。
可是,这样设计的耦合性仍是不够低,隔离性不够强。
程序员小胖:还不够强吗?订单模块和库存模块都在不一样的module了,不费任何力气,就能够直接把源代码分红两个项目,由不一样的团队来写了。
404P: 不够,如今的隔离性最多在开发层,可以相互隔离开发。运行时呢?
全部module的bean都在同一个spring context中,A模块能够任意引用B模块的bean,开发同窗引入另外一个module以后,不太清楚该module中哪些是对外提供的的接口,哪些bean是能够直接注入调用的,哪些Bean是内部Bean,不适合直接去注入的。
长期如此,一个模块的bean被不断地注入到另一个模块被调用,那么其运行时的隔离性就差了。运行时没有作好隔离,是服务拆分的一大痛点。大概就是下图这个样子。
程序员小胖:请继续你的表演。
404P:随着业务的增加,必然会把Order和Stock拆分,成为不一样的子业务系统。代码在不一样的module,拆分开来很简单,可是拆分后两个业务系统是运行在不一样的SpringContext中的。而bean的注入只在一个SpringContext有效,因此以前经过bean注入来实模块交互的地方须要梳理出来,变成系统之间的接口交互才能实现服务拆分。
程序员小胖:听你这么一说,有道理。模块化思想都是跟着架构思想走的啊。若是要考虑将来模块拆分红服务,就须要考虑好运行时隔离,也就是运行时的低耦合交互。
404P: 是的。看看SOFA怎么作的。
为了防止这种模块之间滥用bean注入来交互。SOFA启动后,会为每一个moudule建立一个SpringContext,每一个module运行在各自的SpringContext中。不一样模块之间的bean没法直接引用,具有了较好的运行时隔离能力。
那么当Order模块想引用Stock模块的Bean,怎么办呢?
首先,须要Stock模块有发布对外的公共bean,经过以下声明式发布(也能够经过注解方式):
<sofa:service ref="stockBeanA" interface="com.alipay.sofa.StockBeanA"/>
复制代码
那Order模块怎么引用Stock模块中的公开bean呢?经过以下声明方式引用:
<sofa:reference id="stockBeanA" interface="com.alipay.sofa.StockBeanA"/>
复制代码
Stock模块的stockBeanA已经公开发布,而且Order模块已经引用,那么Order模块在编码的时候,就能够直接注入bean stockBeanA了。
这种方式,咱们能够很清晰地看到一个模块公开了哪些服务,引用了哪些服务。模块之间的交互变得很是清晰。
当一个SOFA应用开始变得复杂,开发团队成员开始增多时,就须要进行服务化了。好比,Order模块和Stock模块,再也不是运行在一个系统了,而是要变成Order系统和Stock系统了。这意味着这两个领域之间的交互从模块级别的交互上升到应用系统级别的交互了。
这种服务化拆分须要考虑两点:
(1)模块化的交互是在同一JVM内存中不一样SpringContext之间的交互,拆分红两个应用系统后,应用系统之间交互必然是经过网络请求来交互,必然要考虑远程通讯的问题。
(2)模块之间的服务发布和引用与应用系统之间的服务发布和引用是否有差别,须要改造?
SOFA考虑了以上两点,要将一个SOFA模块拆成微服务是很是便捷的。
Stock应用发布的时候,以下:
<sofa:service ref="stockBeanA" interface="com.alipay.sofa.StockBeanA">
<sofa:binding.bolt/>
</sofa:service>
复制代码
Order应用引用Stock的服务时,以下
<sofa:reference id="stockBeanA" interface="com.alipay.sofa.StockBeanA">
<sofa:binding.bolt/>
</sofa:reference>
复制代码
能够看出,就是添加了个属性 ,
<sofa:binding.bolt/>
复制代码
表示服务之间的交互是经过sofa bolt远程调用框架来完成,发布和引用方式几乎没有变化。这种简易的服务化拆分,为蚂蚁架构在服务化演进的过程当中带来了很大的便利。
随着问题域的复杂性愈来愈高,模块之间的隔离边界也有更高的要求,本文从简单的例子,逐渐演变到服务拆分,从而引出SOFA的模块化。SOFA基于SpringContext做为模块隔离边界,充分下降了模块交互的耦合性,同时也为后续服务拆分提供了便利。
关于SOFA模块化的实现原理,将另起一文,欢迎关注下方公众号,更多思考,与你分享。
近期文章