為什么用Dubbo而不是Spring Cloud?基于支付場景的微服務高可用架構實戰
今天給大家帶來的分享是基于支付場景的一個微服務實戰,會更偏向于應用層的內容。
主要圍繞以下四點進行分享:
- SOA 與微服務
- 老支付架構遇到的挑戰
- 基于微服務怎么做的改造
- 未來計劃要做的事
SOA 與微服務
在我看來,微服務雖是國外傳進來的技術,卻和咱們中國的一些理論是掛鉤的。所以在正式進入主題之前,先給大家簡單介紹一下麥田理論。
關于麥田理論
古代周朝時期,老百姓種地實際是沒有任何規劃的,也沒有任何區域的限制。
一般來說在地里一會種水稻,一會種小麥,一會種蔬菜地交叉來種,可收成之后發現莊稼受陽光程度非常低,營養非常不均衡,后期維護成本非常高。
直到戰國時期,有一位農業專家把地劃分為多個區域,每一個區域種一種莊稼,地跟地隔開,形成最初的微服務理念。
過去我們看到的很多文章都只是講到 SOA 和微服務之間的比較,我今天在這個基礎上加了一個 DDD。下面就 DDD、SOA 以及微服務的演進過程先做個引子。
DDD、SOA 與微服務
SOA 架構
SOA 是上一個時代的產物,大概是在 2010 年之前出現的,最早提出時是提供給傳統行業計算領域的解決方案,當時 Oracle、IBM 也提了很多方案,包括出現的很多流程引擎。
它的思想是將緊耦合的系統,劃分為面向業務的粗粒度、松耦合、無狀態的服務。
在這之后,微服務的提出者基于 SOA 做了一個改進,就把它變成單一職責、獨立部署、細小的微服務,是一個相反的概念。
微服務與 DDD
今天我們一說到微服務就會想到 DDD,有不少朋友認為 DDD 就是為微服務而生的。其實不是這樣的,我在接觸 DDD 時,它最早是用來做 UML 設計、領域建模的。
DDD 講究充血模型,而 J2EE 模型以傳統的分層架構和 Spring 架構捆綁在一起形成了以貧血模型為主的架構模式。
貧血模型的優點是容易入門、分層清晰,而充血模型要求設計者前期對業務理解較深,不然后期項目會產生混亂。
另外就是 DDD 思想比較寬泛,導致形成百家爭鳴的姿態,沒有形成一套固定的方法論。
開發者不容易理解,所以后面關注 DDD 的人變少了,而微服務的提出巧妙地借鑒了 DDD 里面的限界上下文、子域、領域事件等關鍵詞,在微服務得到越來越多業界認可的情況下,也給 DDD 帶來了重新的煥發機會。
老支付架構遇到的挑戰
判斷項目好壞的兩個角度
我們判斷一個優秀項目的好壞,可以從優秀的代碼和高可用架構兩個方向來講。
我們在設計高可用架構的同時,也不能忽視代碼的重要性,優秀的代碼指的是冗錯能力、冥等操作、并發情況、死鎖情況等,并不一定是指代碼寫得多漂亮。
這就好比蓋樓一樣,樓房的基礎架子搭得很好,但蓋房的工人不夠專業,有很多需要注意的地方忽略了,那么在往里面填磚加瓦的時候出了問題。
后果就是房子經常漏雨,墻上有裂縫等各種問題出現,雖然不至于樓房塌陷,但樓房也已經變成了危樓。
從代碼和設計的角度來看有:
- 由不合理的代碼所引起的項目無擴展性
- 數據庫經常發生死鎖
- 數據庫事務亂用,導致事務占用時間過長
- 代碼容錯能力很差,經常因為考慮不足引起事故
- 程序中打印的大量的無用日志,并且引起性能問題
- 常用配置信息依然從數據庫中讀取
- 濫用線程池,造成棧和堆溢出
- 從庫中查詢數據,每次全部查出
- 業務代碼研發不考慮冪等操作
- 使用緩存不合理,存在驚群效應、緩存穿透等情況
- 代碼上下流流程定義混亂
- 異常處理機制混亂
再從整體架構角度來看:
- 整體依然使用單體集群架構
- 采用單機房服務器布署方式
- 采用 Nginx+hessian 的方式實現服務化
- 業務架構劃分不徹底,邊界模糊
- 項目拆分不徹底,一個 Tomcat 共用多個應用
- 無故障降級策略
- 監控系統不合理(網絡、系統)
- 支付運營報表,大數據量查詢
- 運維手動打包,手動上線
- 系統擴容手動布署
基于以上兩點,我們可以清晰地看到以前老項目的存在問題,并開始思考新的微服務架構應該怎么去做。
基于微服務怎么做的改造
如上圖,想做高可用的微服務架構,必須先確立以下五個點:
- 產品迭代速度,當架構設計完以后一定是對產品有利的,不能設計完了之后反而比以前開發得更慢了。這也是微服務的核心,通過把業務拆遷,將不同的產品一個個進行產品化,而不是項目化。
- 系統穩定性,單體架構時一個系統報錯就全部報錯,現在更是粒度細一些,同時架構各種監控。
- 研發效率。
- 問題快速定位,也就是每年給我們幾十分鐘的故障時間。
- 系統耦合度,不要把系統都放在一起,要多拆開。
利用 DDD 來劃分限界上下文
這是根據一些業務場景做的業務架構圖,中間綠色部分是產品服務層。用 DDD 的思想來分析,產品服務層也就是產品服務域,這個域里包含三個子域,一個是收銀臺子域,一個是商戶子域,一個是個人子域。
每一個域里都包含有限界上下文,收銀臺包含兩個,商戶包含四個,個人包含兩個。
有些同學可能不太了解限界上下文概念,可以把它理解為一個系統、一個邊界或者一個實體。
比如說我們每天上班要倒三次地鐵,這里面的關鍵事件是什么?就是上班,那限界上下文就是坐地鐵,中間切換三次。
限界上下文就可以把它理解為一個微服務,也可以把它理解為一個系統、一個模塊。
限界上下文的劃分可以根據我們的團隊規模來定,如果團隊規模沒有達到一定的程度,可以將邊界定的粗一些,如果項目規模和團隊規模不斷擴大,還可以再把大的領域和限界上下文繼續拆分成多個小的。
微服務治理架構圖
這是我們大致的一個微服務整體流程圖,采用的是 Spring Boot+Dubbo 的架構。
為什么主張用 Dubbo 而不是 Spring Cloud?有兩點原因:
- 要看目前這個架構是什么,如果是 Dubbo,很多組件的一些設施都要圍繞它來做,如果這時把它完全推倒換成別的架構,成本部分我們需要慎重考慮。
- Spring Cloud 技術雖新但不見得一定比 Dubbo 好用。目前公司就維護了一個叫 Dubbo Cloud,自己研發一套基于 Dubbo 的微服務體系。
上圖中間這塊的探針也是我們自主研發的,能夠實現把整個服務鏈路的各種信息采集到。
比如掉網時間、報錯、返回值以及參數全部采集到,采集完之后就把這些信息用一個開源組件進行改造,把信息全部推給它,透過那個界面展示出我們要的東西。
后面這一套比如 Hystrix 熔斷、Dubbo Admin 和 Mock Server,我們是參考 Dubbo 的思路做的智能化攔截和服務降級。
后面的服務注冊、服務發現、服務路由、失敗重試和服務監控等是 Dubbo 本身提供的,也就是圖片綠色的部分是我們自研的新功能,未來我們會把這個 Dubbo Cloud 體系進行開源。
通道報警切換系統的演進
這是我們通道報警系統的框架演進。為什么叫“通道”呢?因為我們的支付要接銀行,但銀行本身是相對偏傳統的,它們的通道不是很穩定。
經常有各種各樣的問題,而每個銀行都有 N 個通道,我們無法得知哪個通道近期是穩定的或不穩定,都是會來回變的。
這里我們自研了一個 Agent,通過采集它通道里的一些使用數據,比如說這次我們連成功了,獲取到了數據,然后放到 Kafka 里。
之后還有一個統計分析的東西,如果這個通道連接成功一次會對它的統計加一次,***把每隔一段時間的結果存到 Redis 集群里。
圖中的路由系統就是做通道選擇的。這是一個業務系統,路由系統每次在做通道選擇時要先從 Redis 集群里把這個銀行的通道拿出來,選出評分***的一個。
拿出后再經過自己的一套路由選舉的配套做一個清洗或者選擇,***得到一個***的通道,直接連到銀行通道上,這樣我們就能知道哪些通道是高可用的。
底下的過程還是通過 Kafka,并進行各種各樣的統計分析,也非常好用。然后這里會有一個圖表,如果當前有問題,在界面上都能可以看到,同時可以給你發短信、發郵件。
為什么我們這里做了兩套?***期的時候我們要做一個數據的比對,因為如果采集的數據不準確,這個通道就會存在問題。
所以我們一方面是通過上面這種方式來做評分策略,另一方面是把數據采集過來以后放到一個庫里,通過這個庫再做一次,***每次做一個比較,以統計正確率。
當這個通道也發生了問題,比如某一個銀行通道發生了問題,我們的監控系統就會直接把銀行通道設置為不可用,然后通知研發部門讓他們去解決,完了以后再把這個通道變為可用。
如果這個過程數據不準確,會造成頻繁的通道切換,會產生很多不必要的問題,所以在***期時我們先做成半自動化的。
雙活體系架構的演進
雙活機房的演進,也是需要兩個階段:
***個階段是偽雙活
- 兩個機房同時提供服務,但需要設置主備機房;備機房的應用只能通過專線訪問主機房的數據庫;備機房的 Redis 也需要通過專線訪問主機房的 Redis。
- 當主機房掛了后,需要先將備機房應用的數據庫配置改成備庫,同時備庫停機,修改備庫為主庫。
第二個階段是泳道雙活
雙活體系架構的演進,在ZK做數據同步的時候,采用兩種方式 Curator 的 TreeCacheListener 監控相應節點的變化從而同步數據,另一個是修改 ZK 源碼偽裝成 Observer 接收事務日志數據從而實現數據同步。
ZK 的同步***還是不進行同步、泳道隔離,比如像使用 Dubbo 這種的時候,完全可以同步二套環境,如果使用當當的 Elastic-Job,在做雙活的時候就會相對麻煩。
微服務架構全景圖
這個就是我們整個微服務的整體架構。圖中左半部分體現了怎么把服務進行劃分,劃分了哪些領域,然后有哪些服務,數據庫內容怎么劃分,網關層怎么做的,是從業務角度來做的一個劃分。
右邊這塊是體現我們如何保障微服務的可靠性。***層主要是給項目運營人員使用,第二層是我們為了保障微服務都做了什么東西,有統一調度中心、雙活管控架構,還有大數據平臺、分布式緩存,做了各種各樣的組件來保障微服務順利的開展。
再下面是一些監控,這里我們用了 APM 分布式調鏈的監控,包括我們自己也做了一些監控平臺。
持續集成測試
接下來講講我們的持續測試經驗。怎么來保證代碼的質量?這里就涉及到了集成測試的概念。
我們分了四個象限:一是單元測試,這是由開發自己來做的,一般覆蓋率在60-80%就不錯了;二是驗收測試和探索測試。
這兩個實際上是我們的測試人員在做,一個是驗證業務的可行性,一個是采取一些非法條件或者是一些破壞性測試,***是壓力測試,通過壓測看系統能承受的負載情況。
這是我們的整個測試流程:
- 首先,我們參照了阿里和其他公司的一些編碼規范,制定一套自己的編碼規范,并跟所有開發人員達成共識。
- 然后我們有自己的靜態代碼檢查,這里也可以用阿里的組件,這是前兩步。
- 第三步就是單元測試,基本上前三部分都是由開發來保障代碼的健壯性和正確性。
- 第四是持續集成,我們根據自己的規則和模板對它再進行一次代碼的掃描,掃描完之后就組織一些架構師或是技術專家,對一些關鍵核心代碼再做一個代碼重構,大致是分了五步。
未來計劃要做的事
上圖幾點就是未來我們計劃要做的一些事情,因為現在業務量越來越大了,而不久后央行會再出一個文件,要考慮異地多活這個情況,這塊我覺得是一種趨勢,目前也有很多公司在做這件事情。
然后我們也會有一個 Dubbo Cloud,未來也是會開源的。后面就是大數據平臺的持續建設,目前我們的大數據平臺還不是特別完善。
像數據的挖掘這塊還沒有做,而現在一般的金融公司還有支付公司的風控都要做得特別好,而我們才剛起步。
程超,智慧支付***支付專家,12 年 Java 開發經驗,對互聯網支付、電商業務方向較為了解,擅長分布式、性能調優等技術領域,對高并發、大數據有濃厚興趣。《深入分布式緩存》一書聯合作者。