莫斯科交易所交易和清算系統架構的演變。 第2部分

莫斯科交易所交易和清算系統架構的演變。 第2部分

這是一個漫長故事的延續,講述了我們創建一個強大的高負載系統以確保交易所運作的荊棘之路。 第一部分在這裡: habr.com/en/post/444300

神秘錯誤

經過多次測試,更新的交易和清算系統投入運行,我們遇到了一個可以寫一個偵探神秘故事的錯誤。

在主伺服器上啟動後不久,其中一筆交易處理時出現錯誤。 然而,備份伺服器上一切正常。 事實證明,在主伺服器上計算指數的簡單數學運算給出了真實參數的負結果! 我們繼續研究,在 SSE2 暫存器中,我們發現了一位差異,該位元負責在處理浮點數時進行捨入。

我們編寫了一個簡單的測試實用程式來計算具有捨入位元設定的指數。 事實證明,在我們使用的 RedHat Linux 版本中,當插入不幸的位元時,在使用數學函數時會出現錯誤。 我們向 RedHat 報告了這一情況,過了一段時間,我們收到了他們的補丁並推出了它。 錯誤不再出現,但不清楚這個位子是從哪裡來的? 該函數負責它 fesetround 來自C語言。我們仔細分析了我們的程式碼以尋找假定的錯誤:我們檢查了所有可能的情況; 查看所有使用舍入的函數; 嘗試重現失敗的會話; 使用具有不同選項的不同編譯器; 使用靜態和動態分析。

無法找到錯誤原因。

然後他們開始檢查硬體:對處理器進行負載測試; 檢查內存; 我們甚至針對一個單元中出現多位錯誤這一極不可能的情況進行了測試。 無濟於事。

最後,我們採用了高能量物理領域的理論:一些高能量粒子飛入我們的資料中心,刺穿機殼壁,撞擊處理器並導致觸發閂鎖卡在其中。 這個荒謬的理論被稱為「中微子」。 如果您遠離粒子物理學:中微子幾乎不與外界相互作用,並且肯定無法影響處理器的運作。

由於無法找到故障原因,為了以防萬一,「有問題」的伺服器被停止運作。

一段時間後,我們開始改進熱備份系統:我們引入了所謂的「熱儲備」(warm)——非同步副本。 他們收到了可能位於不同資料中心的交易流,但 Warms 並沒有主動與其他伺服器互動。

莫斯科交易所交易和清算系統架構的演變。 第2部分

為什麼要這樣做? 如果備份伺服器發生故障,則與主伺服器連接的熱伺服器將成為新的備份伺服器。 也就是說,發生故障後,系統不會保留在一台主伺服器上,直到交易時段結束。

而當新版本系統測試投入運作時,再次出現舍入位元錯誤。 而且,隨著熱伺服器數量的增加,該錯誤開始更頻繁地出現。 同時,由於沒有具體證據,供應商也沒有什麼可展示的。

在接下來的情況分析中,出現了一個理論:問題可能與作業系統有關。 我們寫了一個簡單的程序,在無限循環中呼叫函數 fesetround,記住當前狀態並透過睡眠檢查它,這是在許多競爭線程中完成的。 選擇睡眠參數和執行緒數後,我們在運行該實用程式約 5 分鐘後開始一致地重現位元故障。 然而,紅帽支撐無法重現它。 對我們其他伺服器的測試表明,只有那些具有某些處理器的伺服器才容易受到該錯誤的影響。 同時,切換到新核心解決了問題。 最後,我們只是簡單地更換了作業系統,而導致該錯誤的真正原因仍不清楚。

去年突然在哈布雷上發表了一篇文章“我如何發現 Intel Skylake 處理器中的錯誤」 裡面描述的情況和我們的情況非常相似,但是作者進一步調查並提出了錯誤出在微代碼中的理論。 當Linux核心更新時,製造商也會更新微程式碼。

系統的進一步發展

雖然我們擺脫了錯誤,但這個故事迫使我們重新考慮系統架構。 畢竟,我們無法避免此類錯誤的重複發生。

以下原則構成了預訂系統下一步改進的基礎:

  • 你不能相信任何人。 伺服器可能無法正常運作。
  • 多數保留。
  • 確保達成共識。 作為對多數保留的邏輯補充。
  • 雙重失敗是可能的。
  • 活力。 新的雙機熱備方案應該不會比之前的方案差。 交易應不間斷地進行,直到最後一個伺服器。
  • 延遲略有增加。 任何停機都會造成巨大的經濟損失。
  • 最少的網路互動以盡可能降低延遲。
  • 在幾秒鐘內選擇新的主伺服器。

市場上現有的解決方案都不適合我們,而且 Raft 協議仍處於起步階段,因此我們創建了自己的解決方案。

莫斯科交易所交易和清算系統架構的演變。 第2部分

聯網

除了預訂系統之外,我們也開始實現網路互動的現代化。 I/O子系統由許多進程組成,對抖動和延遲的影響最嚴重。 由於有數百個進程處理 TCP 連接,我們被迫不斷地在它們之間切換,而在微秒級別上,這是一個相當耗時的操作。 但最糟糕的是,當進程收到要處理的封包時,它會將其傳送到一個 SystemV 佇列,然後等待來自另一個 SystemV 佇列的事件。 然而,當存在大量節點時,一個進程中新 TCP 封包的到達和另一個進程中佇列中資料的接收對於作業系統來說是兩個相互競爭的事件。 在這種情況下,如果沒有可用於這兩個任務的實體處理器,則會處理一個任務,並將第二個任務放入等待佇列中。 無法預測後果。

在這種情況下,可以使用動態進程優先權控制,但這將需要使用資源密集型系統呼叫。 於是,我們改用經典的epoll的單線程,這大大提高了速度並減少了事務處理時間。 我們還擺脫了單獨的網路通訊進程和透過SystemV進行的通信,顯著減少了系統呼叫的數量並開始控制操作的優先權。 僅在 I/O 子系統上,就可以節省約 8-17 微秒,具體取決於具體情況。 從那時起,這種單線程方案就一直沿用不變;一個帶有餘量的 epoll 線程足以為所有連接提供服務。

事務處理

我們系統上不斷增長的負載需要升級幾乎所有組件。 但不幸的是,近年來處理器時脈速度成長的停滯已經不再能夠正面擴展流程。 因此,我們決定將引擎流程分為三個級別,其中最繁忙的是風險檢查系統,該系統評估帳戶中資金的可用性並自行建立交易。 但資金可以是不同的貨幣,因此有必要弄清楚應在什麼基礎上劃分請求的處理。

合理的解決方案是將其按貨幣劃分:一台伺服器以美元進行交易,另一台伺服器以英鎊進行交易,第三台伺服器以歐元進行交易。 但如果採用這樣的方案,發送兩筆交易來購買不同的貨幣,那麼就會出現錢包不同步的問題。 但同步既困難又昂貴。 因此,按錢包和工具分別分片是正確的。 順便說一句,大多數西方交易所沒有像我們一樣嚴格檢查風險的任務,因此大多數情況下都是在線下完成的。 我們需要實施線上驗證。

讓我們用一個例子來解釋一下。 交易者想要購買 30 美元,請求進入交易驗證:我們檢查該交易者是否被允許使用這種交易模式以及他是否擁有必要的權限。 如果一切正常,請求將發送至風險驗證系統,即檢查資金是否足以完成交易。 有一條說明,所需金額目前已被凍結。 然後請求被轉送到交易系統,交易系統批准或拒絕交易。 假設交易獲得批准,那麼風險驗證系統就會標記資金已解鎖,盧布就會變成美元。

一般來說,風險檢查系統包含複雜的演算法並執行大量非常消耗資源的計算,並且不像乍看那樣簡單地檢查“帳戶餘額”。

當我們開始將Engine流程分層時,我們遇到了一個問題:當時可用的程式碼在驗證和驗證階段主動使用相同的資料數組,這需要重寫整個程式碼庫。 因此,我們借鑒了現代處理器的一種處理指令的技術:每個指令都被分成小階段,並且在一個週期內並行執行多個操作。

莫斯科交易所交易和清算系統架構的演變。 第2部分

經過對程式碼的小修改,我們創建了一個用於平行事務處理的管道,其中事務被分為管道的 4 個階段:網路互動、驗證、執行和結果發布

莫斯科交易所交易和清算系統架構的演變。 第2部分

讓我們來看一個例子。 我們有兩個處理系統,串行和並行。 第一個交易到達並發送到兩個系統中進行驗證。 第二個事務立即到達:在平行系統中,它立即開始工作,而在順序系統中,它被放入佇列中,等待第一個事務完成當前處理階段。 也就是說,管道處理的主要優點是我們可以更快地處理事務佇列。

這就是我們提出 ASTS+ 系統的原因。

確實,傳送帶也並非一切都那麼順利。 假設我們有一個交易影響相鄰交易中的資料數組;這是交換的典型情況。 這樣的事務不能在管道中執行,因為它可能會影響其他事務。 這種情況稱為資料危險,此類事務只需單獨處理:當佇列中的「快」事務用完時,管道停止,系統處理「慢」事務,然後再次啟動管道。 幸運的是,此類事務在整體流量中所佔的比例很小,因此管道很少停止,不會影響整體效能。

莫斯科交易所交易和清算系統架構的演變。 第2部分

然後我們開始解決同步三個執行緒執行的問題。 結果是一個基於具有固定大小單元的環形緩衝區的系統。 在這個系統中,一切都取決於處理速度;資料不會被複製。

  • 所有傳入的網路封包都進入分配階段。
  • 我們將它們放入一個陣列中,並將它們標記為可用於第 1 階段。
  • 第二筆交易已到達,再次可用於第一階段。
  • 第一個處理執行緒查看可用事務,處理它們,並將它們移至第二個處理執行緒的下一個階段。
  • 然後,它處理第一個事務並標記相應的單元 deleted - 現在可以用於新用途。

整個隊列都是這樣處理的。

莫斯科交易所交易和清算系統架構的演變。 第2部分

每個階段的處理需要單位或幾十微秒。 如果我們使用標準作業系統同步方案,那麼我們將在同步本身上損失更多時間。 這就是我們開始使用自旋鎖的原因。 然而,這在即時系統中是非常糟糕的形式,而且RedHat嚴格不建議這樣做,因此我們應用自旋鎖100毫秒,然後切換到信號量模式以消除死鎖的可能性。

結果,我們實現了每秒約 8 萬筆交易的效能。 兩個月後 文章 關於 LMAX Disruptor,我們看到了具有相同功能的電路的描述。

莫斯科交易所交易和清算系統架構的演變。 第2部分

現在,一個階段可能有多個執行緒。 所有交易均依照收到的順序一一處理。 結果,峰值效能從每秒 18 筆交易增加到 50 筆交易。

交易所風險管理系統

完美無極限,很快我們再次開始現代化:在ASTS+的框架內,我們開始將風險管理和結算營運系統轉移到自治組件中。 我們開發了靈活的現代架構和新的分層風險模型,並嘗試盡可能使用該類 fixed_point 而不是 double.

但一個問題立刻就出現了:如何將運行多年的業務邏輯全部同步並轉移到新系統上? 結果,新系統原型的第一個版本不得不被放棄。 目前正在生產中的第二個版本是基於相同的代碼,適用於交易和風險部分。 在開發過程中,最難做的事情就是兩個版本之間的 git merge。 我們的同事 Evgeniy Mazurenok 每週都會進行這項手術,每次他都會罵很久。

當選擇一個新系統時,我們立即要解決互動的問題。 選擇資料匯流排時,需要確保穩定的抖動和最小的延遲。 InfiniBand RDMA 網路最適合這種情況:平均處理時間比 4 G 乙太網路少 10 倍。 但真正讓我們著迷的是百分位數的差異 - 99 和 99,9。

當然,InfiniBand 也面臨挑戰。 首先,不同的 API - ibverbs 而不是套接字。 其次,幾乎沒有廣泛可用的開源訊息傳遞解決方案。 我們嘗試製作自己的原型,但結果證明非常困難,因此我們選擇了商業解決方案 - Confinity Low Latency Messaging(以前稱為 IBM MQ LLM)。

那麼合理劃分風險體系的任務就出現了。 如果您只是刪除風險引擎並且不建立中間節點,那麼來自兩個來源的交易可以混合。

莫斯科交易所交易和清算系統架構的演變。 第2部分

所謂的超低延遲解決方案具有重新排序模式:來自兩個來源的交易可以在收到時按所需的順序排列;這是透過使用單獨的通道來交換有關訂單的資訊來實現的。 但我們還沒有使用這種模式:它使整個過程變得複雜,並且在許多解決方案中根本不支援它。 此外,每個交易都必須分配相應的時間戳,在我們的方案中,這種機制很難正確實現。 因此,我們使用了具有訊息代理的經典方案,即具有在風險引擎之間分發訊息的調度程序。

第二個問題與客戶端存取有關:如果有多個風險網關,客戶端需要連接到每個風險網關,這將需要對客戶端層進行變更。 我們希望在現階段擺脫這種情況,因此目前的風險網關設計會處理整個資料流。 這極大地限制了最大吞吐量,但極大地簡化了系統整合。

複製

我們的系統不應該出現單點故障,即所有元件都必須是重複的,包括訊息代理。 我們使用CLLM系統解決了這個問題:它包含一個RCMS集群,其中兩個調度程序可以主從模式工作,當一個調度程序發生故障時,系統會自動切換到另一個調度程序。

使用備份資料中心

InfiniBand 針對本地網路的運作進行了最佳化,即用於連接機架安裝設備,且 InfiniBand 網路無法鋪設在兩個地理上分散的資料中心之間。 因此,我們實作了一個橋接器/調度器,它透過常規乙太網路連接到訊息存儲,並將所有事務中繼到第二個 IB 網路。 當我們需要從資料中心遷移時,我們可以選擇現在要使用哪個資料中心。

結果

上述所有工作都不是一次性完成的;開發新架構需要多次迭代。 我們在一個月內創建了原型,但花了兩年多的時間才使其進入工作狀態。 我們試圖在增加事務處理時間和提高系統可靠性之間實現最佳折衷。

由於系統進行了大量更新,我們從兩個獨立來源實施了資料恢復。 如果訊息儲存因某種原因無法正常運作,您可以從第二個來源(風險引擎)取得交易日誌。 整個系統都遵循這項原則。

除此之外,我們能夠保留客戶端 API,這樣經紀人或其他任何人都不需要為新架構進行大量返工。 我們必須改變一些介面,但不需要對營運模型進行重大改變。

我們將平台的當前版本稱為 Rebus - 作為架構中兩個最引人注目的創新的縮寫,Risk Engine 和 BUS。

莫斯科交易所交易和清算系統架構的演變。 第2部分

最初我們只想分配清算部分,但結果是一個龐大的分散式系統。 客戶現在可以與貿易網關、清算網關或兩者互動。

我們最終的成果:

莫斯科交易所交易和清算系統架構的演變。 第2部分

降低了延遲水平。 在交易量較小的情況下,系統的工作方式與先前的版本相同,但同時可以承受更高的負載。

峰值效能從每秒 50 萬筆交易增加到 180 萬筆交易。 進一步的成長受到唯一的訂單匹配流的阻礙。

有兩種進一步改進的方法:並行化匹配和改變它與 Gateway 的工作方式。 現在所有網關都根據複製方案運行,在這種負載下,複製方案將停止正常運作。

最後,我可以給那些正在敲定企業系統的人一些建議:

  • 隨時做好最壞情況的準備。 問題總是在不經意間出現。
  • 快速改造架構通常是不可能的。 特別是當您需要在多個指標上實現最大可靠性時。 節點越多,需要支援的資源就越多。
  • 所有客製化和專有解決方案都需要額外的資源來進行研究、支援和維護。
  • 不要拖延解決系統可靠性和故障後恢復的問題;在初始設計階段就將它們考慮在內。

來源: www.habr.com

添加評論