支付系統一般需要對接多個支付渠道,一是為了保證系統的可靠性,不能因為單一渠道的問題影響整個支付系統。二是為了提高支付能力,不同渠道提供支付能力不同。三是為了降低支付成本。 對接多個支付渠道以後,為了可以正確選擇支付渠道支付,因此設計渠道路由系統。 從上圖可以看到路由系統功能其實很簡單,分發支付請求到 ...
支付系統一般需要對接多個支付渠道,一是為了保證系統的可靠性,不能因為單一渠道的問題影響整個支付系統。二是為了提高支付能力,不同渠道提供支付能力不同。三是為了降低支付成本。
對接多個支付渠道以後,為了可以正確選擇支付渠道支付,因此設計渠道路由系統。
從上圖可以看到路由系統功能其實很簡單,分發支付請求到正確的渠道。但就是這個簡單系統,也經過幾次系統改造升級,最終才成為現在的樣子。下麵就來說說這個系統是如何演進。
下麵假設對接支付渠道為支付寶與微信。
初期
支付系統初期,這個階段業務需求較簡單,僅僅需要滿足一個支付場景(例如使用支付寶支付)。為了快速上線,設計方案就簡單粗暴,對外直接暴露支付服務介面,由業務系統發起直接調用。
系統設計圖如下:
這個階段由於只有一個支付渠道,所以也不需要有路由系統,直接由業務系統調用支付服務介面發起支付。
這個設計方案存在很多問題:
- 業務系統與支付系統位於同一個系統,系統任何一次變更都會影響整個系統。
- 擴展性問題。接入新支付渠道,如微信,需要新暴露一個微信支付服務介面。業務系統需要改動代碼。從另一方面講,業務系統承擔路由系統的功能。
- 復用性。新支付渠道,其實除了與支付渠道交互相關代碼之外,其他代碼可以復用。
針對以上問題,將系統進行了相應改造。
首先是將支付系統與業務系統單獨拆分出來,成為兩套單獨的系統。支付系統對外暴露一組通用介面。業務系統僅對接這組介面。業務系統若想指定支付渠道支付,介面參數傳入渠道標識即可。這樣就將耦合在業務系統中路由功下沉到支付系統。
其次梳理渠道介面文檔,抽象出共性介面。接入新支付渠道,只要繼承介面,實現相關方法即可,簡化渠道開發難度。
改版後的系統實現圖如下:
此時,路由系統知識支付系統的一個模塊,具體實現如下。
首先定義通用渠道介面,其中 channelName 方法,返回渠道渠道唯一標識,如支付寶渠道返回 aliPay。
然後根據 Spring ApplicationContext getBeansOfType 方法,獲取實現同一個介面的所有 Bean.最後將其放入 Map 緩存中,其中鍵值為 channelName 方法返回渠道標識。
這個階段方案的問題在於支付系統所有模塊位於同一工程。有些模塊需要頻繁發佈,而有些模塊,如渠道模塊,路由模塊改動就很少。這樣就導致系統任一改動發佈,影響整個支付系統可用性。
中期
針對初期後面的問題,進行了相應改造。
首先還是進行拆分,將支付系統按照模塊拆分。路由系統,渠道系統,成為獨立系統,獨立部署維護。
系統之間調用採用 RPC 通訊,使用 Dubbo 框架。
相關實現如下:
相關介面邏輯不變,只是將同一進程內調用變成跨系統的調用。
渠道系統提供服務:
這裡改動,將渠道標識放入 Dubbo 服務 group 欄位,藉助 Dubbo 分組功能標識中唯一的渠道系統。
路由系統引用渠道系統的服務:
這裡同樣需要設置 group 且需要和服務提供者一致。然後在路由系統中將服務註冊到緩存中,使用渠道標識為 key,渠道服務名為 value。
最後路由系統藉助 Spring ApplicationContext getBean 獲取具體的服務。
這個設計的問題在於:
路由系統中需要手動引用渠道系統服務,然後再註冊。這樣在增加渠道系統就比較繁瑣。那是不是可以做到增加渠道系統時,無需修改路由系統,路由系統自動發現服務?
藉助 Dubbo API。
後期
查看 Dubbo 文檔,可以直接使用 ReferenceConfig 直接查找服務提供者。
官方文檔建議:
ReferenceConfig 實例很重,封裝了與註冊中心的連接以及與提供者的連接,需要緩存。否則重覆生成 ReferenceConfig 可能造成性能問題並且會有記憶體和連接泄漏。在 API 方式編程時,容易忽略此問題。
這裡使用ReferenceConfigCache,用於緩存 ReferenceConfig 實例。
去除之前所有引用服務配置文件以及緩存註冊代碼,引入 ReferenceConfigCache,改造如下。
總結
回顧上文路由系統,可以看到初期沒有路由系統,整個系統可以運行下去。但是隨著系統複雜度提高,初期系統架構已經不能滿足系統的高效運行,所以才一步步改進系統。改進的過程中,不斷發現方案不足處,然後一步步迭代演進。這個過程中,要善於利用現有框架的功能,加速功能的開發。