1. 程式人生 > >揭祕:螞蟻金服 SOFA 分散式架構演進之路

揭祕:螞蟻金服 SOFA 分散式架構演進之路

640?wx_fmt=png

本文根據黃挺(花名魯直)老師在中生代社群第101期微信群分享整理而成

首發於中生代架構(archthink),經授權轉載

640?wx_fmt=jpeg

架構好文:6000字 | 10分鐘閱讀

01

介紹

_____

大家好,我是螞蟻金服的魯直,是螞蟻金服微服務團隊的  TL,同時也負責 SOFA 對外開源的相關事宜。

非常感謝中生代社群王友強,螞蟻金服右軍的組織,讓我今天能夠有機會給大家做一個分享。我今天給大家帶來的分享是「SOFA 分散式架構的演進」

在開始之前,可能很多人不太清楚 SOFA 是什麼東西,這裡先做下簡單地介紹。SOFA 是螞蟻金服自研的一套金融級分散式中介軟體,從寫下第一行程式碼到今天已經有將近 10 年的時間,包含了應用容器,RPC,訊息,資料中介軟體,分散式事務,限流,熔斷,分散式鏈路追中等等框架,算是一個分散式中介軟體全家桶。隨著螞蟻金服這 10 年業務的飛速發展,SOFA 也在這個過程中得到了大量地錘鍊,快速地成長,支撐了每年雙十一,雙十二,新春紅包等大型活動。大家可以從下面這種圖中看到 SOFA 涵蓋的範圍。在今年 4 月份,SOFA 開始了開源之路,目前已經有部分元件開源在了 Github 上面,歡迎大家圍觀 star:

https://github.com/alipay

640?wx_fmt=png

02

早期模組化

_____

要講 SOFA 的發展過程,要從支付寶的早期開始,在支付寶的早期,支付寶的全站的架構非常簡單,就是一個簡單的分層架構,類似於下面這張圖:

640?wx_fmt=png

最前面是一個負載均衡器,負載均衡器的流量直接打到當時支付寶唯一的系統錢包系統裡面來,然後錢包系統後面連著一個數據庫。這種系統的分層設計在剛開始系統流量不高,團隊不大的時候,沒有太大的問題,但是當團隊規模擴大,團隊內部以及團隊之間的協作成本就會越來越高,所以在 SOFA 最開始的版本中,我們引入了模組化的方案,來為系統解決系統內部的協作的問題,也為服務化做準備。

SOFA 的模組化不同於一般的模組化的方案,在一個一般的模組化的方案裡面,只是在程式碼的組織結構上進行了模組化的拆分,負責同一個功能的程式碼內聚到到一個 Maven 模組下面,最終打包成一個 JAR 包。這種模組化的方案有一個缺陷,就是沒有考慮執行時的問題,在這種模組化的方案裡面,一般上都只有一個 Spring 的上下文,意味著一個模組裡面的 Bean 可以任意地訪問另一個模組裡面的 Bean 而沒有任何控制,長期來看,這種情況會導致模組和模組之間在執行時的高度耦合。

為了解決這個問題,SOFA 的模組化方案給每一個模組都加上了一個獨立的 Spring 上下文,預設的情況下,一個模組不能直接引用另一個模組的 Bean。當需要引用另一個模組的 Bean 的時候,需要在程式碼中通過類似於 RPC 的服務釋出和引用來解決,比如當模組 A 需要呼叫模組 B 的 SampleService 這個 Bean 的時候,模組 B 可以通過以下的程式碼來提供服務:

<sofa:service ref="sampleService" 
interface="com.alipay.sofa.sample.SampleService"/>

另一個模組 B 就可以通過以下的程式碼來引用服務:

<sofa:reference id="sampleService" 
interface="com.alipay.sofa.sample.SampleService"/>

通過 SOFA 的模組化方案改造之後,一個系統的模組可以如下圖所示,圖中的紅線就是 JVM 的服務釋出和引用:

640?wx_fmt=png

03

從單應用到服務化

_____

通過 SOFA 引入模組化的方案之後,在一定程度上幫助業務解決了研發效率的問題。但是隨著業務的不斷地發展,團隊規模的不斷擴大,單純靠一個系統內的模組化已經難以滿足業務的訴求,所以,在這個時期,我們開始了服務化的改造,這個時候,SOFA 之前的模組化的方案的另一個優勢就能夠體現出來了,當我們將一個系統的多個模組通過服務化拆成多個系統的時候,只需要在原來的 <sofa:service/>  以及 <sofa:reference/> 裡面加上一個協議,就可以將本地的模組間的呼叫變成 RPC 的呼叫:

<sofa:service ref="sampleService" 
interface="com.alipay.sofa.sample.SampleService">
                   <sofa:binding.bolt/>    
<sofa:service/>
<sofa:reference id="sampleService" 
interface="com.alipay.sofa.sample.SampleService">

                   <sofa:binding.bolt/>
<sofa:reference/>

在服務化的早期,我們引入了 F5 作為服務間呼叫的負載均衡裝置,也通過 F5 來做服務發現,如下面的架構圖所示:

640?wx_fmt=png

但是,運行了一段時候之後,我們發現 F5 成了一個瓶頸,所有的流量都要 F5,對 F5 本身會造成比較大的壓力,另外,這種負載均衡裝置在處理長連線的時候會有一些問題,服務端擴容操作可能會導致最終流量不均衡,所以,在後面,我們引入了自研的服務註冊中心,變成了下面的這種結構:

640?wx_fmt=png

這裡面之所以不選擇 ZK 的原因是因為考慮到 ZK 是一個 CP 的系統,在發生網路故障的時候,會發生嚴重地不可用。所以 SOFA 自己的服務註冊中心 SOFARegistry 設計成了一個 AP 的系統,最大程度的保證可用性,放棄一定程度的一致性。

目前 SOFARegistry 正在進行開源的準備工作,在準備完成之後,會公佈出來。

到了這個階段,支付寶的系統已經完成了基本的服務拆分。

04

SOFA資料拆分

_____

在通過服務化解決了應用的水平擴容的問題之後,後面,我們遇到了資料庫的容量的問題,原來,支付寶的所有的資料都在一個大的資料庫裡面,首先想到的就是進行垂直的拆分,將不同的業務的資料放到不同的資料庫裡面去。如下圖所示:

640?wx_fmt=png

但是隨著交易量的上升,類似交易庫這種,會首先面臨大量的交易的資料,單庫都放不下這麼多的事情,這個時候,就需要考慮做水平拆分, 比如向交易庫這種,我們就可以根據使用者的 ID 進行拆分,變成類似於下面的這種結構:

640?wx_fmt=png

資料庫的分庫分表我相信很多人都已經聽說過,這裡也分享一下如何確定需要有多少的庫, 需要有多少的表。首先是最小的庫的數量,可以通過業務峰值 TPS 除以單庫容量上限 TPS 來計算。然後是最小的表的數量,可以通過單位時間業務量乘以儲存時長再除以單表的容量上限來進行計算。

05

SOFA分散式事務

_____

在經過資料庫的拆分之後,在金融場景下很自然地,就面臨了一個新的問題,就是分散式事務的問題,原來所有的資料都在一個庫裡面,那麼只需要資料庫支援事務就可以了。在資料庫經過了拆分之後,就需要通過引入分散式事務來協調多個數據庫之間的事務問題了。

在 SOFA 裡面,通過自研了一個 TCC 的框架來解決了分散式事務的問題,也就是現在的 SOFA DT  X ,在 TCC 模型下,會有一個事務的發起方,這個一般上是一個業務系統,它會現在業務系統中去啟動一個本地事務,然後呼叫所有的事務參與方的 Try 介面,如果 Try 通過之後,再呼叫所有的事務的參與方的 Commit 介面進行事務提交。如果 Try 失敗,則呼叫所有的事務參與方的 Cancel 介面進行事務回滾。在 TCC 中,一旦一階段 Try 通過之後,二階段就 Commit 就必須成功,但是現實情況中,總會因為各種各樣的問題,會有 Commit 失敗的情況發生。所以,在 SOFA 的 DTX 中,還有一個單獨的服務,專門用於重試二階段,讓這些事務最終能夠成功。

640?wx_fmt=png

當然,在 SOFA 裡面,除了對分散式事務做同步的服務的事務支援之外,針對非同步的訊息,也提供了事務訊息的支援,SOFA 裡面的事務訊息的支援可以看如下這張圖:

640?wx_fmt=png

在事務訊息裡面,發起方會在一個本地事務中去傳送一個訊息,SOFA 的訊息中心接收到這個訊息之後,會落到訊息中心的儲存裡面,但是這個時候,訊息中心並不會向訂閱方投遞訊息;等到發起方的本地事務結束,會自動給訊息中心一個通知,告訴訊息中心本地事務已經提交或者回滾,如果訊息中心從發起方得到的通知是事務已經提交,就會將訊息傳送給訊息的訂閱方,如果訊息中心從發起方得到的通知是事務已經回滾,那麼訊息中心就會從儲存中將訊息刪除掉。當然,發起方給訊息中心的通知在中間也可能會因為各種各樣的問題到丟失,所以,一般上事務的發起方還需要實現一個訊息回查的介面,當訊息中心在一段時間內沒有收到事務的發起方的通知的時候,訊息中心會主動回查發起方,主動諮詢發起方對應的事務的狀態,根據主動拿到的狀態來決定訊息是要傳送還是刪除。

在螞蟻內部,分散式事務和事務型訊息作為 SOFA 在事務上的解決方案都在被廣泛地使用,其中分散式事務一般上用在強同步的場景,比如轉賬的場景,而事務型的訊息一般上被用在非同步的場景,比如訊息記錄的生成等等。

06

合併部署

_____

在經過了幾年的服務化之後,因為螞蟻的業務的特點,出現了一些比較長的業務鏈路,比如從淘寶過來的支付鏈路,可能中間涉及到十幾個系統,這些系統之間在一次請求中的相互呼叫非常頻繁,導致中間 RPC 消耗地時間比較高,並且像支付鏈路這樣,其實上下游關係非常密切,在運維操作上,因為大促而導致的容量評估,擴容縮容也都必須一起操作,所以,在 SOFA 中,我們引入了合併部署的概念,來解決這種長的業務鏈路中的 RPC 呼叫耗時的問題,也期望通過合併部署能夠讓關鍵系統更好地去做容量評估。合併部署整體的示意圖如下圖所示:

640?wx_fmt=png

所謂的合併部署,從上圖中可以看出,就是將相關聯的一些系統部署到一個 SOFA 執行時下面,每個系統之間通過單獨的 ClassLoader 載入,防止出現類衝突,和服務化的過程剛好相反,在合併部署裡面,SOFA 會自動地將這些系統之間的 RPC 呼叫轉換成 JVM 呼叫,從而節省了類的成本。

在合併部署裡面,有門面系統和非門面系統的概念,只有門面系統會對外暴露服務,外部的系統只能看到門面系統釋出的服務,非門面的系統全部為門面系統服務。

雖然這些系統部署最終是部署在一起的,但是開發還是有獨立的團隊進行開發,所以在研發上,並沒有太大的差比,合併部署更多的是一種運維以及部署上的優化。

07

單元化

_____

剛才在資料拆分的那張圖裡面知道,一個應用對應的資料庫進行了拆分之後,對於一個應用的例項來說,它必須連到分庫後的所有的資料庫,才能夠確保任何請求進入的時候都可以找到資料,這種方式在應用的例項數量增加之後,就會出現資料庫連線的瓶頸,如下圖所示:

640?wx_fmt=png

為了解決資料庫連線的瓶頸的問題,我們開始引入了單元化的概念,在螞蟻叫 LDC(邏輯資料中心),單元化的概念如下圖所示:

640?wx_fmt=png

在單元化中,一個邏輯資料中心只處理一個數據分片的請求,如果說資料是按照使用者來進行分片的話,涉及到一個使用者的請求只會在一個邏輯資料中心裡面來處理,通過這樣的設計,一個應用連的資料庫只要和對應的邏輯資料中心的資料分片一致就可以,這樣,就可以大量地減少資料庫上的連線數的壓力。而且利用這種邏輯資料中心的概念,理論上,如果資料庫的連線數不足,只需要增加邏輯資料中心就可以。

08

ServiceMesh

_____

前面講到的 SOFA 從演進過程中發展出來的能力,都是在螞蟻線上執行地非常成熟的一些東西,這幾年,雖然 Kubernetes 的普及,ServiceMesh 也變得越來越火,所以,SOFA 現在也在往 ServiceMesh 這個方向上演進,在螞蟻內部,有將近 2000 多個 SOFA 的系統,每次版本的升級都非常痛苦;另外,隨著人工智慧等領域的興起,Python 等語言越來越火,原來 SOFA 裡面所有的內容都是通過 Java 來做的,已經不能夠滿足當前業務的系統,我們需要也尋找一個方案去解決多語言的問題。剛好,通過 ServiceMesh,我們可以將 SOFA 裡面原有的一些能力下沉,比如服務發現,限流,熔斷等等,這樣,這些能力的升級一方面可以擺脫業務系統去自主升級,另一方面,也可以讓 SOFA 的體系更加方便地為其他的語言所服務。

目前 SOFAMesh 的 0.1.0 的版本也已經在 Github 上面開源,包括 Istio 的 Control Plane 的部分:

https://github.com/alipay/sofa-mesh

以及我們自研的 Data Plane 的部分:

https://github.com/alipay/sofa-mosn

09

總結

_____

SOFA 的發展離不開螞蟻金服自身業務的發展,正是由於螞蟻金服自身業務的飛速發展,需要 SOFA 不斷地去解決各種各樣的問題,才有了 SOFA 的今天,當然,我們也深感螞蟻金服本身的業務的廣度和整個業界比起來真的是九牛一毛,所以在今年 4 月份,我們開始了 SOFA 的開源的程序,逐步將 SOFA 裡面的各個元件開放出來,希望 SOFA 在整個社群中能夠得到更大的鍛鍊,得到更多的反饋,幫助 SOFA 進一步發展。對 SOFA 開源感興趣的,可以到我們的 Github 的地址上給一個 star: https://github.com/alipay 

感謝大家今天的捧場,因為今天的分享更多的是 SOFA 在發展過程中發展出的一些能力,相對來說沒有這麼深入,相信也有同學希望可以瞭解更加深入的內容,所以,在這裡我把我中間講到的一些內容的對應的深入的講解的文章貼到這裡,大家有興趣可以看看:

素描單元

另外,也歡迎大家關注「金融級分散式架構」公眾號,我們會在裡面去不斷地分享一些關於 SOFA 相關的內容:

640?wx_fmt=png

長按關注,獲取最新分散式架構乾貨

歡迎大家共同打造SOFAStack

https://github.com/alipay

10

Q&A

_____

Q1:為什麼可以減少資料庫連線?

A1: 這裡的意思是說,應用的例項太多,每個例項都需要至少幾個連線,從資料庫的角度看,就是每個例項的連結數量 * 例項數量,有可能會超過資料庫本身能夠承受的連結上限。

Q2:資料庫連線分片,如果按照使用者分,那要是其他欄位,比如訂單的維度來查詢,怎麼路由呢?

A2: 可以在訂單 ID 裡面把使用者的分片資訊寫進去,一個訂單肯定是屬於一個使用者的。這樣對訂單的查詢,最後還是通過使用者分片來路由。

Q3:邏輯資料中心是怎麼解決連線瓶頸的?

A3: 一個邏輯資料中心只處理一部分使用者的請求,所以在這個邏輯資料中心裡面的應用例項,只需要連結對應的資料分片的資料庫就可以,不用全連,這樣資料庫層面的連線數就減少了。

Q4:合併部署,是基於1個JVM還是多個JVM?

A4: 這個是基於一個 JVM 來做的。我們做的就是多個 SOFA 應用部署到同一個 JVM 裡面去,本質上,每個團隊還是在開發自己的 SOFA 應用,和原來單獨部署沒有區別。

Q5:如果補償失敗,後續一般怎麼處理?

A5: 補償失敗就重試,對於 TCC 來說,一定要補償到成功為止。

Q6:阿里已經開源熔斷降級框架,螞蟻這塊兒會開源熔斷降級框架麼?跟SOFArpc做一些整合?

A6: 這塊我們在準備好了之後會考慮開源,目前阿里開源的 Sentinel 我們也會考慮在 SOFARPC 裡面整合。

Q7:maven實現的模組化, 不光是相互呼叫, 也可以做成分層結構,  這樣比較簡單。 如果service之間的呼叫也用sofa,會不會太重?  畢竟是在一個微服務裡面

A7: 這個需要看情況,如果業務真地非常簡單的話,可以一個 Spring 上下文就搞定了。如果真的有一定的複雜度,為了更好的區分模組,可以通過 SOFA 這樣的模組化的方式,我的分享中貼的是 XML 的方式,其實 SOFA 也提供了註解的方式釋出服務,引用服務,程式碼寫起來其實非常簡單。

Q8:假如在分散式事務裡分支事務失敗了,那DTX應該是可以保證分支事務的重試,不用從整個大事務上做重試,並且後臺會用Job定期撈需要重試的事務進行重試吧?

A8: 是這樣的

Q9:SOFA開源的這些全家桶都是強耦合的嗎?能否單獨使用嗎?

A9: 每一個都可以單獨使用

640?wx_fmt=png

中生代社群出品

好書推薦,技術管理者必學必看

做技術要懂管理 做管理要學模式

640?wx_fmt=jpeg

640?wx_fmt=png

經典回顧