超越“雙十一”—— ebay百萬TPS支付賬務系統的設計與實現

導讀

2018年,ebay全面展開了下一代百萬TPS支付賬務系統的設計與實現。本文主要介紹核心賬務系統的性能和容災能力,將從賬務系統簡介、百萬TPS壓測實驗、系統架構分析、開源計劃四個方面進行闡釋。希望能給同業人員一定啓發和借鑑。

1. 序

億貝 (www.ebay.com) 於2018年全面展開了下一代支付系統的設計和實現。支付寶的雙十一和微信財付通的春節紅包已經向世界展示了支付行業當前的工程成果。那麼作爲下一代支付系統,如何才能做得更好?支付寶的支付系統能支撐雙十一期間高達幾十萬的併發量,我們的支付系統有沒有可能支持得更高、更穩定?俗話說擒賊先擒王,支付系統最重要的組件之一是賬務系統,有着極其嚴格的業務正確性和穩定性要求,我們便以此爲突破點,開展了攻堅項目

我們支付團隊在2018年底接到項目後對支付行業做了充分的調研,首先確定了下一代賬務系統的設計目標:

1. 業務目標
   a. 支持所有支付業務功能
   b. 擴展接口支持所有互金功能
2. 容災目標
   a. 三地五中心部署,數據中心級別容災
   b. 數據毫秒級實時異地同步備份,業務秒級異地主備切換
3. 性能目標
   a. 自動線性擴容
   b. 有能力承接全球所有支付公司峯值流量之和
4. 數據目標
   a. 實時數據中臺
   b. 具備一定的區塊鏈所具有的數據防篡改能力

接下來我們開展了爲期半年的系統設計和原型實現。2019年,第一季度原型系統通過驗收,下一代核心賬務系統正式立項。經過半年緊鑼密鼓的開發,系統於今年第三季度正式灰度上線,並通過了性能和容災驗收。

本文主要介紹核心賬務系統的性能和容災能力,包含以下內容:

  1. 核心賬務系統簡介
  2. 百萬TPS壓測實驗
  3. 系統架構分析
  4. 開源計劃

2. 核心賬務系統簡介

2.1 業務簡介

支付系統爲賬戶間的資金流動提供系統性的解決方案。支付系統在處理轉賬業務的時候需要根據當前賬戶資金及流水情況對資金流動的金額、方向、業務合理性等做必要的業務校驗。所有這些核心轉賬業務和所有賬戶的資金及流水的維護均由核心賬務系統負責。支付系統幾乎所有的支付能力均建立在覈心賬務系統之上,因此核心賬務系統是最重要的系統之一

2.2 系統簡介

賬戶資金是賬戶當前的狀態,每一筆資金流動都會對這些狀態進行查詢和修改。當用戶數和業務數超過一定規模後,單臺機器就無法滿足系統的性價比要求,因此互聯網行業普遍採用了集羣化的多機解決方案。但多機方案會遇到很多單機方案所沒有的挑戰

  1. 如何將狀態劃分至多臺機器
  2. 多臺機器之間的連接出現問題時如何正確維護狀態
  3. 單個數據中心出現故障時如何保障業務的正確性和穩定性
  4. 跨城數據備份如何處理網絡延時問題
  5. 系統如何快速地擴容和縮容
  6. 如何保證分佈在多節點上的數據不會被惡意篡改

核心賬務系統還面臨着很多其它的挑戰,這些挑戰會隨着系統流量的增加而變得更加嚴峻。當系統出現量變時,我們需要尋求質變。接下來讓我們看一看下一代的核心賬務系統如何應對量變和質變的挑戰

3. 百萬TPS壓測實驗

3.1 實驗目標

下一代核心賬務系統的首要設計目標是更好地支持電商業務。電商業務通常會面臨秒殺等短時超大規模用戶請求,此時系統需要具備瞬時線性擴容能力來確保用戶體驗。爲此我們設計了百萬TPS壓力測試來驗收下一代核心賬務系統的相關能力。

3.2 實驗意義

支付寶的雙十一和微信的春節紅包等項目在十萬TPS的問題上已經有了成熟的解決方案。但業界對於百萬甚至更高TPS的問題尚無代表性的系統案例。百萬TPS意味着支付寶、微信財付通、國際卡組織、國際第三方支付等全球所有支付系統的線上峯值流量之和。由於支付業務與人類活動有關,存在物理上限,解決了百萬TPS的問題意味着一勞永逸地解決了賬務系統的基礎架構問題

另外,系統性能數量級的提升往往意味着架構的迭代更新,我們藉此機會就老的問題提出新的解法,希望這些新的想法能應用於更多其它系統,解決更多其它行業的問題。

3.3 實驗流程

實驗分爲兩個部分:第一部分驗證單組節點的性能,業務場景爲單商戶秒殺業務,系統驗證點爲單組節點能支持的最高TPS;第二部分驗證多節點集羣的性能,業務場景爲主站大促,系統驗證點爲當TPS請求逐步上升時系統的自動擴容能力。

3.4 系統準備

核心賬務系統在正式生產環境中的部署方式爲三地五中心,本次壓測採用了簡化版的兩地三中心部署。每組爲3個節點,跨城部署在2個數據中心。壓測共準備667組,合計2,001個業務節點。周邊輔助節點爲2,350個。一共準備4,351個節點。這些節點總帶寬爲128Gbps,總cpu core數量爲14,767個,總內存爲45T,總硬盤存儲空間爲84T。所有硬件消耗3個機櫃,共300臺物理機,總成本爲450萬美元/3年

3.5 實驗結果

3.5.1 單組節點測試

水電煤繳費、用戶還款、電商秒殺等業務均有大量用戶需要在指定時間點對單一賬戶做資金轉入操作。由於單個賬戶已無法再做分庫分表處理,單組節點的性能上限就決定了單個賬戶的交易量上限。因此,我們需要實施單組節點壓測,以瞭解單個賬戶的限制情況。

3.5.1.1 兩地三中心結果

圖3.1

在這個實驗中,三個數據節點部署在兩個異地數據中心,通過基於Raft共識算法的強一致性協議進行實時數據同步。這是最常見的一種部署方案,兼顧了容災和業務處理響應時間。從圖3.1的測試結果看,1KB的請求,每秒最高能處理的交易量是7,800筆。

值得一提的是,當達到峯值後,由於請求接收階段的處理能力已到上限,P99(第99個百分位數)下單個轉賬請求的延時明顯增大。而批處理日誌寫入階段單次打包的日誌數量明顯上升,部分抵消了海量請求帶來的影響,P90下延時的增大幅度則相對較小。

在整個測試過程中發生了一次Raft選主,導致了TPS的降低。這是因爲新主上臺前需要將自身的業務狀態追到最新,才能處理新的轉賬請求。和傳統基於Raft協議的應用相比(例如KVStore),這是支付應用較爲獨特的地方。

3.5.1.2 同城三中心結果

圖3.2

本次實驗爲同機房三節點部署。從圖3.2可以看到,系統最高每秒可處理9,500筆交易。由於數據複製過程中只要有一個從節點(Raft follower)成功寫入日誌即可認爲交易完成,而同數據中心內主從之間數據複製的時延僅爲跨數據中心的1/12到1/8,所以每秒交易數相應增加。該效果與異地三中心部署時主從在同一數據中心的效果一致。另一點值得指出的是,由於同數據中心內網絡和跨數據中心相比更穩定,在整個實驗過程中沒有出現選主,TPS更加平穩

3.5.1.3 單中心結果

圖3.3

本次實驗採用單節點部署,來測試數據複製對交易吞吐量的影響。和前兩次相比,該部署由於只要主節點(也是唯一的一個Raft節點)本地寫入成功即可認爲交易完成,省去了數據複製的時間。從圖3.3可以看到,該部署下每秒交易能穩定在9,700筆,最高能處理將近1萬1千筆。可見,單節點部署和同機房三節點部署的系統吞吐能力幾乎一致,這在一定程度上歸功於我們在提升網絡吞吐量方面作的優化。

3.5.2 多組節點測試

在實際應用中,單組節點往往無法滿足大型電商平臺對總交易量的實時性要求,因此需要部署多組節點,只讓每組節點承擔一部分賬戶的交易請求。我們測試了系統在兩個典型的業務場景下的表現

3.5.2.1 流量階梯上升,節點數量動態調整

圖3.4

該實驗模擬的業務場景是主站日常流量漲跌,交易量會隨着某些外部事件而發生動態變化,例如全站大促時,網站流量會隨着人們作息而發生緩慢震盪。

從圖3.4可以看出,實驗開始只有222組節點,當系統監測到單組節點的平均吞吐量開始上升並超過閾值時(意味着交易量上升,即將達到並超過單組節點的處理能力),系統會自動部署新的節點來分擔總體交易量。伴隨着節點數量的上升,平均吞吐量開始緩慢下降到閾值以下,同時保證總體交易量能被及時處理。該彈性擴容過程示意如下:

系統能動態調整節點數以自適應流量的變化,保證業務不受影響的同時也可以提高硬件的利用率。例如當交易量較低時,可以通過合併節點來減少資源。

3.5.2.2 瞬時超高流量,節點數量不變

該實驗模擬的是主站大促,例如每年雙11的零點,流量洪峯會在極短的時間內涌向支付系統。我們分別測試了系統在瞬間收到50萬(圖3.5)和100萬(圖3.6)交易量的極端情況下的表現。

圖3.5

圖3.6

從圖中可以看出,在這兩種情況下,系統均能從容穩定應對,雖然其中部分節點出現了換主(圖3.5),但對總體流量的影響微乎其微。測試過程中甚至還發現延時有短暫下降,這主要是由於批處理階段打包效率提升。

在達到峯值流量時我們還隨機查看了部分節點的資源使用情況,發現CPU並沒有被打滿,部分線程並未滿負荷運作。這說明流水線上各階段的處理能力強弱不一,後續調優後系統應該會有更好的表現。

4. 系統架構分析

4.1 整體架構設計

4.1.1 核心設計原則

在解決超大流量時,電商系統及支付系統目前最流行的解決方案是通過分庫分表來做流量切分,通過分佈式事務解決分庫分錶帶來的分佈式一致性的問題。這是一個經過了實踐檢驗的解決方案。

但是近十年來經過開源社區和各大互聯網廠商的不斷努力,數據系統的設計有了長足的發展,涌現出了更多更好的解決方案。由於我們是從零開始設計一個新的系統,沒有歷史負擔,所以決定應用更爲領先的系統設計來解決核心賬務系統所面臨的業務和系統複雜度問題。

4.1.1.1 業務複雜度處理

金融系統是一個典型的業務複雜系統。我們計算過,對於信用卡業務來說,一共有573,099,840種不同的業務場景。傳統解決方案是所有系統直接基於數據庫表來進行開發。傳統方案的問題在於數據庫存儲的是數據的二進制格式,沒有業務邏輯,系統在使用數據庫的時候需要基於二進制形式來重構業務邏輯,正確性難以保障。

所幸隨着本世紀初領域驅動設計(Domain Driven Design)模式的出現,金融系統複雜度得到了系統性的解決。我們首先使用了領域模型來對金融業務建模,使得數據的業務表現層和存儲層分離。其次使用Event Sourcing來保證業務處理的正確性和對歷史狀態的百分百可追溯可還原。再者使用CQRS來實現系統讀寫分離。最後用流處理的方式自上而下、從外及內地統一所有設計。

4.1.1.2 多機事務問題

系統在做水平擴容時需要處理數據正確性問題。傳統的方式一般採用分佈式事務。我們基於賬務系統的特殊業務邏輯提出了基於消息最終一致性和業務補償的解決方案

多機方案除了正確性外還有一些其它需要注意的問題。首先是多了至少一次的網絡開銷,增加了請求延時。其次,系統內部流量翻番,單機提供的業務TPS減半。最後由於基於消息最終一致性的實現無法同步返回結果給調用者,系統需要增加異步轉同步的組件。

4.1.1.3 單機事務問題

多機事務需要每臺機器都具備本地事務的能力。傳統數據庫爲了提高事務的執行性能普遍採取了多線程的併發執行機制。但是本世紀初的一些研究發現,對於有些業務場景,特別是金融行業,多線程並非最優解決方案。因此在覈心賬務系統的設計過程中,我們確保核心賬務業務邏輯只在單線程執行,以便充分利用單線程執行所帶來的事務保證和性能優勢

單線程帶來的另一個好處是線性一致性(Linearizability)。事務只能保證多任務按照某種順序來串行化執行(Serializability),在有多種可選順序的情況下,事務調度算法並不保證每次均能選擇同一種確定順序。和事務的一致性不同,線性一致性能確保每次調度的順序完全一致。賬務系統要求所有記賬均按照一個確定的順序記錄,在發生系統回滾及重放後需要恢復至相同順序,因此線性一致性是一個對於賬務系統正確性來說非常重要的保證。

4.1.1.4 系統性能

電商及支付系統通常採用基於SOA的節點間服務調用和基於Java Bean的節點內組件調用。但是在出現秒殺等場景時需要做一些特殊的削峯填谷工作。削峯填谷通常使用消息隊列來做緩衝,因此消息隊列也成爲了一個必不可少的組件

與經典設計中所採用的部分異步實現不一樣,我們在設計時全面使用了基於消息的異步解決方案,包括系統間和系統內的功能調用。異步方案帶來的挑戰是沒有同步接口,提高了調用方複雜度,因此需要利用前文提到的異步轉同步組件來降低調用方複雜度。

4.1.1.5 系統容災

金融系統對容災有極高的要求。首先是數據需要支持同城和異地備份,確保數據有區域級災備能力。再者需要支持業務的快速自動主備切換,時間就是金錢。

傳統方案是基於數據庫自帶的異地備份,分爲異步和同步兩種備份方式

  1. 如果是異步備份,由於數據並沒有被及時同步至備份節點,主節點出問題時會有數據丟失。
  2. 如果是同步備份,則主備機必須同時在線,任何一臺出現問題都會導致業務中斷。備機越多,當中任何一臺出問題的概率越大,業務中斷的概率也越大,因此同步備份的問題在於數據容災能力越強,系統越不穩定。

我們採用了新的方式來達到同時提高數據容災和系統穩定性的效果。新的方法和同步備份一樣需要備機同步返回備份結果,但只要求大部分返回即可,不需要全部返回。這個方法稱爲共識算法族(consensus)。我們選取了其中的Raft算法,從零開始實現自己的高效定製化的強一致性算法。

賬務系統的核心數據會通過強一致性算法實時同步至3個數據中心的5個存儲節點。系統只要有多於一半的節點能正常工作便能整體正常運行,因此該部署計劃允許5個節點中任意2個節點同時出現問題。由於單個數據中心出現問題時最多隻能影響2個節點,因此該部署計劃能支持數據中心級別的災備要求。該部署俗稱三地五中心部署。三地五中心的簡化版爲兩地三中心,也是我們在此次壓力測試中所採用的模型,如圖4.1所示:

圖4.1

我們創新性地採用了雙層(核心業務層和基礎架構層)一體的設計來實現業務的快速自動主備切換。當數據出現災備切換時,定製化的Raft算法會同時切換數據和業務邏輯至災備節點。

爲了能更好地證明系統的容災能力,我們在生產環境部署了定時自動容災演練系統。該系統會不定時的對系統進行隨機的可用性攻擊,從而驗證生產環境中面對網絡、硬盤、內存等出現異常情況時系統的容忍能力。

4.1.2

分層設計

核心賬務系統從上至下分爲4層

  1. 賬務業務層
  2. 基礎賬務層
  3. 基礎架構層
  4. 存儲層

另外還有一個縱向貫穿始終的監控層,負責收集監控數據和跨層監控。架構示意圖見圖4.2:

圖4.2

最上層的賬務業務層提供外部訪問接口,負責處理最上層的業務邏輯,有一定的對外I/O請求,內部無狀態。業務會被分解爲多個條件轉賬請求發給下層的基礎賬務層。基礎賬務層負責記賬,對外無I/O請求,內部有狀態。該狀態由基礎架構層提供的狀態機來維護。基礎架構層負責提供一個基於Raft算法的高效穩定的狀態機實現,其性能由單線程保證,穩定性由強一致性算法來保證。基礎賬務層處理完的結果爲會計賬本,會輸出給存儲層。完整的分層架構圖如下圖4.3所示:

圖4.3

4.2 Event Sourcing

4.2.1 原理

基礎賬務層和基礎架構層是根據Event Sourcing的原則來設計的,這也是核心賬務系統最重要的架構設計。Event Sourcing有4個主要組成部分

  1. Command:外部請求
  2. Event:事件
  3. State:狀態
  4. State Machine:狀態機

所有外部請求會先被髮送給狀態機。狀態機會結合當前狀態生成事件。事件被存入事件倉庫(Event Store)後會被狀態機執行,進而改變狀態機內的狀態。如圖4.4所示:

圖4.4

圖4.5展示了多個事件處理時的時序圖:

圖4.5

舉一個賬務系統的例子。賬務系統接收到的一個命令是「從甲轉乙100美金,但甲不能出現餘額不足的情況」。當前狀態爲「甲有150美金,乙有50美金」。狀態機在收到命令後檢查當前狀態情況,確認轉賬後不會出現甲餘額不足的情況,因此生成事件「甲轉出100美金,乙轉入100美金」。

注意,狀態機對於同一個命令可能生成不同的事件,完全取決於當前狀態。還是剛纔的例子,如果此時甲餘額只有50美金,轉賬100美金的命令將無法被成功執行,此時狀態機會生成一個轉賬失敗事件。

一旦事件被生成並存入事件倉庫,該事件就一定會被狀態機執行,進而改變狀態機內的狀態。對於本例子來說,甲開始狀態爲150美金,在轉出100美金後的結束狀態爲剩餘50美金。

命令和事件會按順序記錄並處理。這個過程是一個經典的數據庫回滾日誌(WAL,Write Ahead Log)案例,也是賬務系統的本地數據存儲格式。由於單機機器故障可能會導致日誌丟失,我們採用Raft強一致性算法來增強數據容災能力。日誌首先會被存儲爲臨時狀態,只有當日志被安全備份到多於一半的機器後纔會被更改爲已提交狀態,允許外部訪問和處理。

數據存儲格式爲數據庫回滾日誌,當系統出錯後會進行標準的數據庫回滾和恢復過程。如圖4.6所示:

圖4.6

4.2.2 CQRS

CQRS(Command Query Responsibility Segregation),即俗稱的讀寫分離,也是Event Sourcing提出來的一個概念。命令和事件會改變狀態機的狀態,是一個寫的過程。事件一旦生成便不可修改,同時事件之間有嚴格的先後順序關係,用戶可以通過順序監聽事件來重構狀態的備份,甚至增加自己的特殊邏輯來生成定製化的視圖,這便是一個讀的過程。

例如核心賬務系統的所有事件爲記賬消息,我們通過監聽消息來生成餘額狀態,並將其保存至關係型數據庫和Kafka等數據流系統做實時聚合。

4.2.3 流處理

我們在CQRS的基礎上更進了一步。鑑於命令和事件都是通過消息系統來傳輸,加之CQRS設計模式中寫和讀兩個操作之間也是通過消息系統來傳輸,一個自然的整體解決方案即爲:用流處理的架構來設計整個賬務系統的消息傳送和處理

各個處理節點,比如處理事件的節點或者是生成視圖的節點,均可以看作流處理的計算節點。流處理的最上層節點通過強一致性算法來保證數據和狀態的高可用及一致性。但這些通過了強一致性同步的數據在向下遊流動的過程中依然可能會出現丟失。但因爲最上游數據有線性一致性保證,下游能通過檢測線性一致性來判斷是否出現了數據丟失,進而通過查詢上游來進行數據補償。這意味着下游數據並不一定需要強一致性算法,我們可以選用一致性稍弱的消息系統,比如Kafka,來作爲下游的消息管道。如圖4.7所示:

圖4.7

4.3 系統實現

4.3.1 實現

前幾章提到了系統的分層。業務層主要處理賬務業務邏輯,爲無狀態節點,容易橫向擴展,由Java實現。核心業務層和基礎架構層負責處理和維護業務狀態,爲有狀態節點,有穩定性、強一致性及性能表現等需求,由C++實現。其餘輔助性組件由go實現。

我們選取了C++17標準,利用了新標準下的一些新功能,並在實現過程中大量使用了函數式編程的思想。使用函數式編程思想的主要目的是確保核心邏輯的可組合性及行爲可重現性,同時也確保核心數據結構在多線程情況下的正確性。

4.3.2 優化

優化主要集中在C++部分,主要優化兩點:1. 吞吐量;2. 延時。我們創新性地採用了雙層(核心業務層和基礎架構層)一體的設計,並結合流水線和批處理,在提高系統吞吐量,降低延時的同時避免了常見的因爲雙層分離由網絡造成的各種狀態不一致的問題。爲了保證該設計實現的正確性,我們使用Jepsen測試框架,並針對性地設計了測試用例,來確保在各種異常情況發生時,系統均能正常工作。

提高系統吞吐量的常用方法爲流水線。把對外部請求的處理過程劃分成若干個階段,每個階段獨立運行,階段之間使用隊列連結,上一個階段的輸出是下一個階段的輸入,形成一條流水線。這樣設計的好處在於可以針對每個階段的特點做獨立優化。例如在數據接收階段,使用多線程來提高單位時間內請求接收的數量;在數據同步階段,採用批處理方式來降低網絡開銷;在覈心處理階段,每個請求必須順序執行以保證狀態正確,因此我們採用了一些僅在對衝基金高頻交易系統中才會使用的特殊優化方法來提高處理速度。

系統延時主要發生在網絡請求上。觀測到的一次跨數據中心請求延時在幾毫秒左右,發生網絡抖動時的延時在幾十毫秒左右。正常的一次網絡請求對用戶體驗沒有大的影響。

但是Event Sourcing會帶來兩個問題。

第一個問題是Event Sourcing處理每個業務事件時需要實時同步災備兩次:第一次備份Command,Command備份完成後再備份Event。這兩次備份有先後依賴關係,無法同步處理。

另一個問題是多個業務事件彼此之間需要按順序先後處理,如果亂序處理會帶來業務的不正確性。圖4.8列舉了2個事件共4步順序網絡同步過程:

圖4.8

通過一些特殊的數據結構優化和運行順序調整,我們成功地優化掉了絕大多數網絡請求。最後結果爲:對於同一批需要處理的事件,系統只需要執行一次網絡同步。我們也論證了在多線程情況下強一致算法在多節點集羣換主時,各個狀態機均能運行或回滾至正確的狀態。優化後的2個事件共1步順序網絡同步過程如圖4.9所示:

圖4.9

4.3.3 部署

系統部署在基於docker和k8s的雲平臺,支持一鍵雲部署。同時由於系統對所有IO進行了抽象和封裝,能通過分佈式配置服務動態切換存儲媒介,系統在部署時能選擇遠程部署,或者完全基於本地單機文件系統的部署,即俗稱的單機版本。單機版本包含了完整的業務、數據、存儲、監控等系統,極大地方便了系統的開發與上線。

5. 開源計劃

我們會分三步來逐步開放系統功能和代碼

  1. 對高校等科研單位開放源代碼
  2. 對商業合作伙伴開放功能和源代碼
  3. 對所有人開源

目前我們已經和英美幾所大學開展了深入的科研合作,以核心賬務系統爲研究對象進行分佈式系統前沿的研究。我們和商業合作伙伴的合作也正在同步進行當中。我們會於明年年底開源所有的代碼和文檔,敬請期待。

6. 跋

核心賬務系統在設計和實現過程中遇到了非常多的挑戰,也得到了很多幫助。感謝億貝支付管理層的信任和戰略支持。感謝億貝中國研發中心管理層的跨部門合作。感謝億貝全球雲計算團隊和SRE團隊的大力支持,在實驗過程中贊助了所有的雲資源,並幫忙實施了大量優化工作。感謝Raft作者Diego Ongaro對系統架構的建議和意見。這只是億貝支付的一小步,我們還有更廣闊的空間去探索。

本文轉載自公衆號eBay技術薈(ID:eBayTechRecruiting)

原文鏈接

https://mp.weixin.qq.com/s/sbHPxzIqv_dRn555XzE5mw

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章