電影《我們的秘密宇宙:細胞的隱密生活》劇照
投資業務是銀行業最複雜的領域之一,因為不僅有貸款、借款和存款,還有證券、貨幣、商品、衍生性商品以及結構性產品形式的各種複雜性。
最近,我們看到人們的金融知識有所提升。 越來越多的人參與證券市場的交易。 個人投資帳戶不久前出現。 它們允許您在證券市場進行交易並獲得減稅或避稅。 所有來到我們這裡的客戶都希望管理他們的投資組合並即時查看報告。 此外,這種投資組合通常是多產品的,也就是說,人們是不同業務線的客戶。
此外,俄羅斯和外國監管機構的需求也在不斷增長。
為了滿足當前的需求,並為未來的升級奠定基礎,我們開發了基於Tarantool的投資業務核心。
一些統計數據。 Alfa-Bank的投資業務為個人和法人實體提供經紀服務,以提供在各種證券市場進行交易的機會、儲存證券的存管服務、為擁有私人和大型資本的個人提供信託管理服務、為其他公司發行證券的服務。 Alfa-Bank的投資業務包括每秒超過3筆報價,這些報價是從各個交易平台下載的。 在工作日期間,代表銀行或其客戶在市場上完成了超過 300 萬筆交易。 外部和內部平台每秒執行多達 5 個訂單。 同時,所有客戶,無論是內部還是外部,都希望即時查看他們的頭寸。
底
從2000年代初開始,我們的投資業務領域開始獨立發展:交易所交易、經紀服務、貨幣交易、證券和各種衍生性商品的場外交易。 結果,我們陷入了功能井的陷阱。 這是什麼? 每個業務線都有自己的系統,這些系統彼此重複功能。 每個系統都有自己的資料模型,儘管它們使用相同的概念來操作:交易、工具、交易對手、報價等等。 隨著每個系統的獨立發展,各種各樣的技術出現了。
此外,系統的程式碼庫已經相當過時,因為有些產品起源於1990年代中期。 在某些領域,這減慢了開發過程,並且出現了效能問題。
新解決方案的要求
企業已經意識到技術改造對於進一步發展至關重要。 我們被賦予了任務:
- 將所有業務資料收集在單一快速儲存和單一資料模型中。
- 我們不得丟失或更改此資訊。
- 有必要對數據進行版本控制,因為監管機構隨時可能要求提供前幾年的統計數據。
- 我們不能只是帶來一些新的、時尚的DBMS,而是要創造一個解決業務問題的平台。
此外,我們的建築師也設定了自己的條件:
- 新的解決方案必須是企業級的,也就是說,它必須已經在一些大公司進行了測試。
- 解決方案的操作模式應該是任務關鍵型的。 這意味著我們必須同時存在於多個資料中心,並在一個資料中心發生故障時從容地度過難關。
- 系統必須可水平擴展。 事實是,我們目前所有的系統都只是垂直可擴展的,並且由於硬體能力的低增長,我們已經達到了天花板。 因此,我們需要一個水平可擴展的系統才能生存的時刻已經到來。
- 除此之外,我們被告知解決方案必須便宜。
我們遵循標準路線:制定要求並聯絡採購部門。 從那裡我們收到了一份總體上準備好為我們做這件事的公司名單。 我們向每個人講述了這個問題,並收到了其中六人對解決方案的評估。
在銀行,我們不相信任何人的說法;我們喜歡自己測試一切。 因此,我們招標競爭的一個強制條件就是通過負載測試。 我們制定了負載測試任務,六分之三的公司已經同意自費實施基於記憶體技術的原型解決方案來進行測試。
我不會告訴您我們如何測試所有內容以及花了多長時間,我只是總結一下:負載測試中的最佳性能是由 Mail.ru Group 開發團隊基於 Tarantool 的原型解決方案展示的。 我們簽署了協議並開始開發。 Mail.ru Group 有四名人員,Alfa-Bank 有三名開發人員、三名系統分析師、一名解決方案架構師、一名產品負責人和一名 Scrum Master。
接下來,我將告訴您我們的系統是如何發展、如何演變、我們做了什麼以及為什麼這樣做。
設計
我們問自己的第一個問題是如何從目前系統取得資料。 我們認為 HTTP 非常適合我們,因為目前所有系統都透過 HTTP 發送 XML 或 JSON 來相互通訊。
我們使用 Tarantool 內建的 HTTP 伺服器,因為我們不需要終止 SSL 會話,而且它的效能對我們來說已經足夠了。
正如我已經說過的,我們所有的系統都存在於不同的資料模型中,並且在輸入時我們需要將物件帶到我們自己描述的模型中。 需要一種允許資料轉換的語言。 我們選擇命令式Lua。 我們在沙箱中運行所有資料轉換程式碼 - 這是一個安全的地方,運行的程式碼不會超出這個地方。 為此,我們只需載入所需的程式碼,創建一個具有無法阻止或刪除任何內容的功能的環境。
轉換後,必須檢查資料是否符合我們正在創建的模型。 我們討論了很長時間的模型應該是什麼以及用什麼語言來描述它。 我們選擇 Apache Avro 是因為語言簡單且有 Tarantool 的支援。 新版本的模型和自訂程式碼可以每天多次投入運行,無論是否有負載,都可以在一天中的任何時間運行,並且可以非常快速地適應變化。
驗證後,必須保存資料。 我們使用 vshard 來做到這一點(我們有地理分散的分片副本)。
此外,由於特殊性,大多數向我們發送資料的系統並不關心我們是否收到資料。 這就是我們從一開始就實施修復隊列的原因。 這是什麼? 如果因為某個原因沒有進行資料轉換或驗證,我們仍然確認接收,但同時將該物件保存在修復佇列中。 它是一致的,位於主業務資料倉儲中。 我們立即為其編寫了一個管理員介面、各種指標和警報。 因此,我們不會丟失資料。 即使來源發生了變化,如果資料模型發生了變化,我們也會立即偵測到它並進行調整。
現在您需要學習如何檢索已儲存的資料。 我們仔細分析了我們的系統,發現 Java 和 Oracle 的經典堆疊必然包含某種將資料從關聯式資料轉換為物件資料的 ORM。 那麼為什麼不立即以圖表的形式將物件提供給系統呢? 所以我們很高興採用了 GraphQL,它滿足了我們所有的需求。 它允許您以圖表的形式接收數據,並僅提取您現在需要的數據。 您甚至可以非常靈活地對 API 進行版本控制。
我們幾乎立即意識到我們提取的數據還不夠。 我們創建了可以連結到模型中的物件的函數 - 本質上是計算欄位。 也就是說,我們將某個函數附加到該字段,例如計算平均報價。 而請求資料的外部消費者甚至不知道這是計算欄位。
實施了身份驗證系統。
然後我們注意到我們的決定中明確了幾個角色。 角色是一種功能聚合器。 通常,角色具有不同的設備使用:
- T-Connect:處理傳入連接,CPU 受限,記憶體消耗低,無狀態。
- IB-Core:透過Tarantool協定轉換接收到的數據,即對錶進行操作。 它也不存儲狀態並且是可擴展的。
- 儲存:只儲存數據,不使用任何邏輯。 該角色實作最簡單的介面。 得益於 vshard 的可擴展性。
也就是說,使用角色,我們將叢集的不同部分相互解耦,從而可以相互獨立地進行擴展。
因此,我們創建了一個非同步事務資料流記錄和一個帶有管理介面的修復佇列。 從業務角度來看,記錄是異步的:如果我們保證將資料寫入自己,無論在哪裡,那麼我們都會確認它。 如果未確認,則表示出現問題,需要傳送資料。 這就是異步錄製。
測試
從專案一開始,我們就決定嘗試實施測試驅動開發。 我們使用 tarantool/tap 框架在 Lua 中編寫單元測試,並使用 pytest 框架在 Python 中編寫整合測試。 同時,我們讓開發人員和分析師都參與編寫整合測試。
我們如何使用測試驅動開發?
如果我們想要一些新功能,我們會嘗試先為其編寫測試。 當我們發現錯誤時,我們確保先編寫測試,然後才修復它。 一開始這樣工作很難,員工會產生誤解,甚至破壞:“我們現在趕緊修復它,做一些新的事情,然後用測試覆蓋它。” 只是這個「以後」幾乎永遠不會到來。
因此,你需要強迫自己先寫測試,然後再要求別人來做。 相信我,測試驅動開發即使在短期內也會帶來好處。 你會感覺你的生活變得更輕鬆。 我們認為 99% 的程式碼現在已經被測試覆蓋了。 這看起來很多,但我們沒有任何問題:測試在每次提交上運行。
然而,我們最喜歡的是負載測試;我們認為它是最重要的並且定期進行。
我將告訴您一個關於我們如何對第一個版本之一進行第一階段負載測試的小故事。 我們將系統安裝在開發人員的筆記型電腦上,打開負載,每秒獲得 4 筆交易。 對於筆記型電腦來說效果很好。 我們將其安裝在由四台伺服器組成的虛擬負載台上,比生產中的效能弱。 部署到最低限度。 我們運行它,得到的結果比在筆記型電腦上的一個執行緒的結果還要差。 內容震撼。
我們非常難過。 我們查看伺服器負載,但結果發現它們是空閒的。
我們給開發人員打電話,他們向我們這些來自 Java 世界的人解釋說 Tarantool 是單線程的。 它只能被負載下的一個處理器核心有效使用。 然後,我們在每台伺服器上部署了盡可能數量的 Tarantool 實例,開啟負載,每秒已收到 14,5 個交易。
讓我再解釋一下。 由於角色劃分使用資源的方式不同,我們負責處理連接和資料轉換的角色僅載入處理器,並且與負載嚴格成比例。
在這種情況下,記憶體僅用於處理傳入連接和臨時物件。
相反,在儲存伺服器上,處理器負載增加,但比處理連接的伺服器慢得多。
記憶體消耗與載入的資料量成正比增長。
服務
為了將我們的新產品專門開發為應用程式平台,我們創建了一個用於在其上部署服務和程式庫的元件。
服務不僅僅是在某些領域運行的小段程式碼。 它們可以是相當大且複雜的結構,是叢集的一部分,檢查參考數據,運行業務邏輯並返回回應。 我們也將服務模式匯出到 GraphQL,消費者收到資料的通用存取點,並對整個模型進行內省。 非常舒服。
由於服務包含更多功能,我們決定應該有一些函式庫來移動常用程式碼。 我們將它們添加到安全環境中,之前已檢查過它不會對我們造成任何破壞。 現在我們可以以函式庫的形式為函數分配額外的環境。
我們希望擁有一個不僅用於儲存而且還用於運算的平台。 由於我們已經有了一堆副本和分片,因此我們實作了分散式計算,並將其稱為 MapReduce,因為它的結果與原始 MapReduce 類似。
舊系統
儘管我們的舊系統支援該協議,但並非所有舊系統都可以透過 HTTP 呼叫我們並使用 GraphQL。 因此,我們創建了一種允許將資料複製到這些系統中的機制。
如果我們發生某些變化,儲存角色中會觸發唯一的觸發器,並且包含更改的訊息最終會進入處理佇列。 它使用單獨的複製器角色發送到外部系統。 該角色不儲存狀態。
新的改進
您還記得,從業務角度來看,我們進行了非同步錄製。 但後來他們意識到這還不夠,因為有一類系統需要立即接收有關操作狀態的回應。 因此我們擴展了 GraphQL 並添加了突變。 它們有機地融入了現有的資料處理範式。 對我們來說,這是另一類系統的讀寫的單點。
我們也意識到,僅服務對我們來說還不夠,因為每天、每週、每月需要建立一次相當繁重的報告。 這可能需要很長時間,並且報告甚至可能阻塞 Tarantool 的事件循環。 因此,我們創建了單獨的角色:調度程序和運行程序。 跑步者不儲存狀態。 它們執行我們無法即時計算的繁重任務。 調度程序角色監視這些任務的啟動時間表,這在配置中進行了描述。 任務本身與業務資料儲存在同一位置。 當正確的時間到來時,調度程序會接受任務,將其交給某個運行者,由運行者對其進行計數並保存結果。
並非所有任務都需要按計劃運行。 有些報告需要按需閱讀。 一旦該需求到達,就會在沙箱中建立任務並將其傳送給運行器執行。 一段時間後,用戶會收到非同步回應,表示所有內容均已計算完畢並且報告已準備就緒。
最初,我們堅持儲存所有資料、對其進行版本控製而不刪除它的範例。 但在人生中,時不時還是要刪除一些東西,大部分是一些原始或中間資訊。 基於expiration,我們創建了一種清除儲存中過時資料的機制。
我們也知道,遲早會出現記憶體中沒有足夠空間來儲存資料的情況,但無論如何,資料都必須儲存。 為了這些目的,我們很快就會製作磁碟儲存。
結論
我們從將資料載入到單一模型的任務開始,並花了三個月的時間來開發它。 我們有六個數據供應系統。 Lua 中整個轉換為單一模型的程式碼大約有 30 萬行。 大部分工作仍在進行中。 有時相鄰團隊缺乏動力,並且有很多情況使工作變得複雜。 如果您曾經面臨類似的任務,請將您認為正常的實施時間乘以三甚至四。
還要記住,業務流程中的現有問題無法使用新的 DBMS 來解決,即使是一個非常有效率的 DBMS。 我的意思是說? 在我們的專案開始時,我們給客戶留下了這樣的印象:現在我們將帶來一個新的快速資料庫,我們將生存! 過程會更快,一切都會好起來的。 事實上,技術並不能解決業務流程存在的問題,因為業務流程就是人。 而且您需要與人合作,而不是與技術合作。
測試驅動開發在早期階段可能是痛苦且耗時的。 但即使在短期內,當您不需要做任何事情來進行回歸測試時,它的正面效果也會很明顯。
在開發的各個階段進行負載測試極為重要。 你越早發現架構中的缺陷,就越容易修復它,這將為你節省大量的時間。
盧阿沒有什麼問題。 任何人都可以在其中學習編寫:Java 開發人員、JavaScript 開發人員、Python 開發人員、前端或後端。 甚至我們的分析師也寫了它。
當我們談論我們沒有 SQL 的事實時,人們會感到害怕。 「如何在沒有 SQL 的情況下取得資料? 那可能嗎? 當然。 在 OLTP 類別系統中,不需要 SQL。 有某種語言形式的替代方案可以立即返回到面向文件的視圖。 例如,GraphQL。 還有一種分散式計算形式的替代方案。
如果您了解需要擴展,請在 Tarantool 上設計您的解決方案,使其可以在數十個 Tarantool 實例上並行運行。 如果你不這樣做,以後將會很困難和痛苦,因為 Tarantool 只能有效地使用一個處理器核心。
來源: www.habr.com