互聯網金融是最近幾年的長期風口,它經歷了野蠻生長期,目前處於強監管期,2016 年 8 月 24 日出臺的《網絡借貸信息中介機構業務活動管理暫行辦法》中明確要求「網絡借貸信息中介機構應當實行自身資金與出借人和借款人資金的隔離管理,並選擇符合條件的銀行業金融機構做爲出借人與借款人的資金存管機構。」git
截至 2017 年 5 月 17 日,正常運營平臺共有 396 家正常運營平臺宣佈與銀行簽定直接存管協議,約佔同期 P2P 網貸行業正常運營平臺總數量的 17.89%,其中有 209 家正常運營平臺與銀行完成直接存管系統對接並上線,佔 P2P 網貸行業正常運營平臺總數量的 9.44%。這就意味着 2017 年 8 月份以後,可能 80% 多的 p2p 平臺(約 2000 家平臺)都要面臨淘汰或者整改。由於,除了對平臺的體量規模有要求,還對平臺的技術實力也是一次大考驗。github
首先人人聚財平臺採起的是直接存管形式,直接存管纔是真正符合《暫行辦法》要求的銀行資金存管的方式,在這種模式下,客戶的資金不在平臺流轉,可以有效隔離平臺與投資人的資金,平臺全程沒法觸碰資金,杜絕了資金池的產生。算法
人人聚財於 2016 年 10 月底上線銀行存管系統,已穩定運行 8 個多月,天天處理異步消息 200 萬條,峯值 400 萬條,人人聚財的技術架構通過了 1.0 版本的所謂的巨石(Monolith)系統,發展到了 2.0 版的分佈式版本,總體技術架構以下圖所示:數據庫
在此架構下,咱們來看看,實現銀行存管的過程當中有哪些關鍵點。網絡
數據一致性如何解決 訂單化機制架構
平臺天天都與銀行有着很是頻繁的交互,如何保證平臺與銀行之間的數據一致呢?這個問題本質上是屬於分佈式事務的問題,業界通常的解思想方案有:2PC(兩階段提交)、TCC(Try/Confirm/Cancel)、事務消息等等,這些方案優缺點本文不做涉及,咱們來看看人人聚財是如何應對這一問題的。併發
咱們知道,分佈式應用具備 CAP 特性,通常來說,不論電商,類電商,仍是社交應用,都會適當犧牲 C,保證最終一致 (Eventually Consistency)和 AP,咱們的作法是平臺與銀行的全部接口進行交互均採用訂單化機制。框架
咱們把每個與銀行的操做交互抽象爲一個訂單,訂單化機制就是指交互開始的時候平臺插入 N(N>=1)個訂單, 這 N 個訂單根據業務的不一樣多是一個父的訂單,也多是父子模式的訂單,每當接口或業務完成的時候就更新對應的訂單的業務狀態。異步
當交互期間因網絡或其餘緣由致使掉單時,平臺的補單機制能夠及時根據當前訂單的業務狀態來判斷接下來應該從哪裏開始補單。好比說在給用戶帳戶進行返現時因爲網絡緣由致使超時了,那麼補單機制會先去銀行查詢該訂單的狀態,根據業務處理狀態來進行下一步操做;若因爲銀行系統異常處理失敗了,那麼補單機制會自動更換流水號從新發起請求,整個交互過程以下圖所示:分佈式
定時數據覈對
平臺天天晚上固定時間點會主動覈對平臺用戶帳戶的可用餘額、凍結金額等數據,並與銀行的用戶帳戶數據進行比對,對於有異常數據會及時通知到相關責任人進行排查。
對帳
對於涉及到資金交互的系統,對帳是必不可少的。平臺天天固定時間點會主動去銀行獲取前一天的對帳文件,與平臺數據進行覈對。
流水號如何生成?
在與銀行的交互過程當中,必須使用一個惟一的流水號來標識一次請求操做,否則業務操做會亂做一團,在對比了一些優秀的惟一 ID 生成算法後,最終選擇使用 Twitter 的 Snowflake 算法來爲咱們提供銀行存管中的流水號服務。此算法的好處有兩點:一是純數字,二是總體上來講是按時間順序的。
原理不做過多介紹,網絡上的文章也不少,算法原版是 Scala 寫的,網上也有 Java 版本,可是有 bug,會產生重複的 ID,沒法用於生產環境的集羣部署,我對其進行了修改,修改後,可用於生產環境,已穩定運行 8 個月,下面是完整核心代碼:
https://gist.github.com/xishuixixia/f0f8684805d0504289b7a40f3b327dd6
須要注意的是,集羣機器需關閉 NTP 的時間同步功能,至於緣由,留給讀者,看上面代碼便知。
任務調度平臺如何落地?
在金融行業中,必定會存在大量定時任務,即在一個特定的時間點,系統執行指定的一個或多個業務邏輯,諸如跑批,對帳等等。
爲何要用任務調度平臺
業界優秀的調度框架當屬 Quartz,比較成熟,可是它有一個不足,就是任務的執行和調度嚴重耦合,代碼裏面有不少硬編碼,很容易出 bug,所以構建一個任務調度平臺就顯得頗有必要。
怎樣搭建任務調度平臺
指導原則是輕量化,不須要過重,咱們在 Quartz 的基礎上,將任務調度行爲抽象造成「調度中心」公共平臺,而平臺自身並不承擔業務邏輯,「調度中心」按照調度配置發出調度請求。「調度」應用和「任務執行」應用之間用 HTTP 協議進行通信,所以,「調度」和「任務執行」兩部分相互解耦,提升系統總體穩定性和擴展性;即便某個任務執行模塊宕機也不會影響其它業務。總體架構以下圖所示:
咱們業務模塊只須要提供「任務執行」接口,任意部署到一臺機器上,而後調度任務平臺配置調用業務接口的基本 Meta 信息(如調用地址端口,bean 名字,監控報警郵件地址)便可。須要注意的是,咱們在開發「任務執行」接口必須保證四個特性:
可重複執行:即冪等性, 數學表示爲:f(f(x))=f(x), 在此處是指調度模塊執行屢次,不會影響業務自己邏輯。
可延遲執行:若是調度任務在指定的時間沒有調度。能夠經過手動執行來彌補。業務執行結果和指定時間執行的結果一致。
可併發執行:若是調度任務同時調用 A B 兩臺業務服務(處理相同業務)。這個時候須要咱們業務須要保證事務一致性。
可暫停、可恢復執行:隨時能夠暫停正在執行的任務,也能夠恢復繼續執行。
固然業界也有一些比較優秀的任務調度系統,在此不做對比。
如何提高性能
下面以定投寶(人人聚財平臺專有的一款智能投顧產品,如下簡稱定投寶)債權轉讓業務爲例來具體說明。
平臺天天都有一部分定投寶產品到期,須要將本金和利息返還給用戶。在用戶購買了平臺的定投寶產品且定投寶複審經過後,用戶的資金就會匹配並投資到對應的借款項目中去。而因爲定投寶的期限相對於借款項目來講較短,以及二者的到期時間不同,所以在定投寶到期的時候借款項目並未到期,所以定投寶只有將用戶持有的債權轉讓出去以後才能夠退出,才能將資金返還給用戶。
在匹配之初,基於爲用戶的投資風險考慮,用戶投資定投寶的資金被打散成很小的粒度分散投資到不一樣的借款項目中去了,所以在定投寶到期須要退出的時候會有大量的債權須要轉讓出去。在接入銀行以前,平臺只須要處理自身的數據便可。而在接入銀行以後,平臺嚴重依賴銀行的操做結果。接入之初,定投寶債權轉讓須要經歷兩個階段,一個是出讓,用於業務校驗,另外一個是轉讓,進行轉讓動做。其中出讓是同步接口,轉讓是異步接口。
通過壓力測試和數據觀察發現大部分時間都消耗在出讓階段,這種模式下沒法知足平臺業務峯值下的業務要求。因而,在與銀行屢次溝通討論的狀況下,銀行取消了出讓接口。同時平臺自身也優化了定投寶債權轉讓模塊,經過 MQ 來接收異步回調消息、代碼重構、優化相關的 SQL 語句以及由本來的單線程模式轉爲線程池模式,大大的提升了業務處理效率。
經過上面這個例子,咱們知道平臺主要經過如下幾個方面來提升業務處理性能:
數據庫層面:優化 SQL 語句,提升執行效率;
代碼層面:優化代碼結構,使用線程池來提升並行處理能力,使用 MQ 來對系統進行解耦並提升異步處理能力;
業務層面:在業務的合理性與效率方面進行權衡,對不合理的接口 say no。
http://mp.weixin.qq.com/s/FNBoppyJKF4t-TqxIzOsfg