螞蟻SOFA系列(1) - 聊聊SOFA的模組化

404p發表於2019-09-04
作者:404,轉載請註明出處。

SOFA是螞蟻自研的一套金融級分散式中介軟體,目前正在逐步向業界開源。SOFA的全稱有兩個,最早是Service Oriented Fabric Architecture,即面向服務的架構。隨著2018年的開源,其全稱改為Scalable Open Financial Architecture,即可擴充套件的開源金融架構。

SOFA技術棧包含了微服務架構體系的各類元件,主要包括RPC框架,服務註冊中心,分散式鏈路追蹤,Metrics監控度量等。

本文我們來聊聊SOFA的模組化。

一 什麼是模組化

模組化在計算機領域是經常討論的話題,在學校學程式語言的時候,教科書上說程式設計要遵循模組化原則。

模組化程式設計是指在進行程式設計時將一個大程式按照功能劃分為若干小程式模組,每個小程式模組完成一個確定的功能,並在這些模組之間建立必要的聯絡,通過模組的互相協作完成整個功能的程式設計方法。

上面這段話引自百度百科,其實精煉下就是:高內聚和低耦合

file

二 模組化思想演變

1 程式碼設計模組化

最早學程式設計的時候,實現一個功能,所有邏輯放到一個main函式裡去,後來發現理不清了,就把main裡面的邏輯抽成幾個函式。這是模組化嗎?是。這個模組化是最基本的程式碼設計能力,增強程式碼的可讀性、可維護性和可擴充套件性。如下圖,計算成績的時候,沒有把所有邏輯放在A中,而是分在B1和B2中,單獨計算,最後A呼叫B1、B2來實現。

file

在這種簡單的程式設計中,往往更注重邏輯的內聚性,只要內聚做好了,往往就是低耦合的。

2 業務領域模組化

在真實做一些專案的時候,業務系統比較複雜,要實現的功能很多。這個時候出現了橫向和縱向的模組化設計。橫向的就是分層設計,縱向的就是按照不通的業務領域來設計。

一個業務系統,橫向的模組化切分主要分為三大層:Web層,Service層,DAL層。

在業務初期,功能往往都是寫在一個業務系統的,比如訂單模組Order、庫存模組Stock。在maven中這些模就是不同的module,但執行都是在同一個JVM,同一個web容器中的。

file

這在種情況下,訂單服務依賴於庫存服務怎麼辦?訂單模組的pom中引入庫存模組的依賴。然後注入bean。

public class OrderService {
    @Autowired
    private StockService stockService;
}

這種橫縱模組化思想隨著業務的複雜開始進化了。訂單、庫存模組雖然在一個Service層,但屬於明顯不同的領域,已經被分成不同的模組了,具有很好的內聚性,而且要引用其它模組,必須引入pom依賴之後才能訪問,具有不錯的隔離型。

3 業務系統模組化的弊端

但是,這樣設計的耦合性還是不夠低,隔離性不夠強。

程式設計師小胖:還不夠強嗎?訂單模組和庫存模組都在不同的module了,不費任何力氣,就可以直接把原始碼分成兩個專案,由不同的團隊來寫了。

404P: 不夠,現在的隔離性最多在開發層,能夠相互隔離開發。執行時呢?

所有module的bean都在同一個spring context中,A模組可以任意引用B模組的bean,開發同學引入另一個module之後,不太清楚該module中哪些是對外提供的的介面,哪些bean是可以直接注入呼叫的,哪些Bean是內部Bean,不適合直接去注入的。

長期如此,一個模組的bean被不斷地注入到另外一個模組被呼叫,那麼其執行時的隔離性就差了。執行時沒有做好隔離,是服務拆分的一大痛點。大概就是下圖這個樣子。

file

程式設計師小胖:請繼續你的表演。

404P:隨著業務的增長,必然會把Order和Stock拆分,成為不同的子業務系統。程式碼在不同的module,拆分開來很簡單,但是拆分後兩個業務系統是執行在不同的SpringContext中的。而bean的注入只在一個SpringContext有效,所以之前通過bean注入來實模組互動的地方需要梳理出來,變成系統之間的介面互動才能實現服務拆分。

程式設計師小胖:聽你這麼一說,有道理。模組化思想都是跟著架構思想走的啊。如果要考慮未來模組拆分成服務,就需要考慮好執行時隔離,也就是執行時的低耦合互動。

404P: 是的。看看SOFA怎麼做的。

三 SOFA模組化

為了防止這種模組之間濫用bean注入來互動。SOFA啟動後,會為每個moudule建立一個SpringContext,每個module執行在各自的SpringContext中。不同模組之間的bean無法直接引用,具備了較好的執行時隔離能力。

file

那麼當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模組拆分成微服務

當一個SOFA應用開始變得複雜,開發團隊成員開始增多時,就需要進行服務化了。比如,Order模組和Stock模組,不再是執行在一個系統了,而是要變成Order系統和Stock系統了。這意味著這兩個領域之間的互動從模組級別的互動上升到應用系統級別的互動了。

file

這種服務化拆分需要考慮兩點:

(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模組化的實現原理,將另起一文,歡迎關注下方公眾號,更多思考,與你分享。


file

近期文章:

分散式冪等問題解決方案三部曲

Java跨平臺?慎用這些有平臺差異性的方法!

相關文章