微服务架构与领域驱动设计应用实践
本篇文章一共分為三個部分,分別是微服務架構的演進過程、具體實踐微服務的應用技術和領域驅動設計的意識轉變。微服務架構已經滲透到互聯網應用的方方面面,而領域驅動設計也逐漸被業界所接收。
微服務架構幾乎都是從 ALL IN ONE 的單體架構演進而來,中間又經歷了分布式架構、面向服務架構的演進過程。
單體架構往往以煙筒式方式發展,往往存在兩個主要問題:中心化和耦合度高。所謂中心化,就是數據集中存儲在單個數據庫中,業務系統集中部署在單臺服務器上,通過集群部署方式提供服務能力,然而中心化的問題,也就是單點問題。而耦合度高,主要是指其中一個功能模塊升級,其它的模塊都得一起升級。這里要說明下,模塊依賴度高不是單體架構的錯,是因為本來架構可能就沒有設計好,但是,在實際場景中,隨著快速迭代開發,研發換了一波又一波,產品走了一茬又一茬,難免系統架構腐化嚴重。
看到了單體架構的諸多問題,系統開始通過按功能或模塊進行拆分,拆分成多個獨立的子系統,系統間通過 RPC、MQ 方式調用,由此逐漸演變為分布式的服務架構。分布式服務架構主要通過服務化和層次化進行解耦拆分,《架構整潔之道》書中提到一點,系統可以降解為策略和層次,而架構設計就是把相同的策略分到同一個組件中,反之,分屬于不同的組件。所以,架構設計可以通過分層設計,由高層次服務調用低層次服務,低層次服務通過接口向上提供服務,以實現系統之間的解耦。也正是在這一階段,單體架構一下子被拆分成幾個或幾十個系統。
但是隨著架構的發展,問題又接踵而來,在沒有做好邊界的劃分之前,系統拆分的服務往往是松散的。正如《架構整潔之道》所講:軟件架構設計本身就是一門劃分邊界的藝術。所以,很多系統在這一階段,又往往進行了服務內聚和去層次化的演變過程。所謂服務內聚,就是以領域驅動設計為原則,重新界定領域邊界,對模塊進行服務整合,對系統進行合并。而去層次化,則是去除服務層次高低之分,按服務調用最優鏈路提供服務請求,降低深度,以此保證系統的穩定性能。
系統如何從 0 到 1,從 1 到 2,從 2 到 100,在具體實踐微服務的過程并不容易。首先,考慮的第一個問題是:微服務是什么?其實,微服務是一個動作:拆。說到拆,就涉及到兩個問題,第一,怎么拆?第二,拆到什么程度。
第一個問題,關于怎么拆,需要關注兩個層面,一是發版速度,如商品、交易、促銷等不同領域的迭代速度和開發速度是不一致的,所以,應該拆分到不同的領域,否則,就可能耦合在一起,A 上線 B 必須跟著一起上,這可能就應該合并到一起。另一個是能源協同,每個系統后面對應不同的研發團隊,一個項目往往需要幾個團隊分工協作,那么系統拆分必然導致團隊協作的成本,所以拆分系統同樣也是拆分團隊,要考慮協作溝通所帶來的成本。
第二個問題,拆到什么程度。其實,已經有很多人對微服務進行了定義,其中我認為最重要的兩點:一是微服務或一組微服務,應該是一套獨立部署運行的服務,二是微服務所依賴的數據資源應該是彼此間相互獨立隔離部署的。
其實,拆只是實踐微服務的開始,要搭建整體的微服務框架還要進行四部曲:拆、服務化、高可用、隔離。
1,服務拆分
微服務講究拆分,那么多微才是微,以及,怎么才是比較合理的拆分。在架構設計領域,有一個大家都在講的著名定律:康為定律,可以理解為:一個系統架構的組織關系是和團隊的組織關系相匹配的。如交易系統會對應一個交易系統的研發團隊,商品系統會對應一個商品系統的研發團隊,這種職責明確的組織結構會使得系統開發更高效。
(圖片來自網絡)《未來架構》一書中講過一個 AKF 立方體模型,從三個緯度講述功能拆分、水平擴展、數據分區所產生的復雜度,如果只有一個系統,單臺部署,單點存儲,那么它就應該只是一個點,因為隨著量級的增大,系統在每個緯度不斷延長,逐漸成為一個龐大的立方體。
(圖片來自網絡)拆分可以分為系統拆分、功能拆分和讀寫拆分。如一個單體系統中,按照用戶的交互場景看,門戶首頁可以拆分為前臺系統,類目、商品、搜索等功能可以拆分到商品中心,訂單、結算、發票等功能則可以拆分到交易中心,這就是簡單的系統拆分和功能拆分。而有些功能較為特殊,如商詳頁,在讀取時需要聚合讀取,所以又可以進行讀寫拆分。
2,服務化
微服務首先需要有微服務基礎設施,沒有微服務基礎設施,實踐微服務就是一場災難。以 SOA 落地方式為例,SOA 落地方式主要有:分布式服務化和集中式管理(ESB),分布式服務化的技術手段有 dubbo 或 spring cloud 等等,必須有整套的如服務發現、服務訂閱、服務監控、服務追蹤、服務日志等微服務基礎設置,才能進行微服務架構。不能簡單只有 p2p 的服務調用就開始服務化,那是不現實的。
3,服務治理
當服務拆分的設計方案確認完畢,而服務化的基礎設施也部署到位,那么系統往往一下子就會突然發布出成百上千個服務接口,就像一個網絡一樣,每個服務或微服務就像網中的一個節點,彼此之間關聯和聯系。但是,服務網絡應該被設計成為一個有向無環圖,否則,就是一團麻。如開始的時候,只是 A 調用 B,但隨著業務發展,需要修改,但是因為對 A 不了解,就加個環節 B 調用 A,如此形成了環形調用,不僅邏輯復雜了,還降低了穩定和性能。
這里的服務治理不是講中間件團隊的服務治理,如超時優化、啟動優化等等,而是作為應用平臺對服務的治理。服務治理又分為三個階段:服務梳理、服務界定和服務編排。所謂服務梳理就是梳理系統對外開放的服務化接口,包括服務的 provider 和 consumer,以及服務分組、動態路由等依賴的梳理,然后對拆散的服務進行歸類、界定,確定服務領域從屬性,依據領域模型重新界定服務邊界,最后通過服務遷移、切換,對同一領域的服務接口進行服務整合,提供統一的服務出口,實現服務編排。
為什么要進行服務治理?那先來看看不進行服務治理的壞處。
微服務化拆分必然會在初期產生代碼到處拷貝,沒有一套代碼,必然會造成復雜的擴散,如同樣語意的 A、B 兩個接口,如果 A 接口存在性能問題需要加緩存,那么 B 接口也會存在同樣的問題,并需要同樣的改造,這樣復雜度就會到處蔓延,同時也無法實現統一的服務層架構,還有,如果同樣語意或相似語意的接口太多,那么接口就是混亂的,無法實現有限接口的治理,如果系統出了問題,可能很難定位問題。業務邏輯是依賴于數據的,如果接口是混亂的,那么慢 SQL 等質量差的 SQL 就無法進行有效收口改造,同樣就更加難以實現數據庫拆分和解耦。綜上,服務治理的過程就是從無序到有序的過程。
總結一下看從拆分到服務化的過程,就是將原來一個整體的服務打碎,碎成一塊一塊的零碎服務,然后再對服務重新歸類、整合,形成一個一個新領域的、獨立的服務。如單體架構就像一個宏偉的城堡,如果相對其中一個點進行調整和改造,那么可能面臨著牽一發而動全身的風險。
微服務化就是以一系列小的服務去支撐一個應用的方法論。簡明扼要的說,就是:分而治之。
4,服務高可用
服務拆分并服務化之后,是不是就完事了,不!真正的微服務實踐才剛剛開始,簡單提供了一個接口,是沒有意義的,它必須具備高可用、高性能、高并發的三高特性,尤以高可用最為重要。保障服務高可用的方法有很多,如數據異構、多級緩存、超時與重試、熔斷、異步并發、降級、限流、消息隊列、壓測與預案等。
重點說下數據異構,所謂數據異構,就是將通過順序消費或并發消費的方式,訂閱 MySQL 數據庫的 binlog,然后通過消息,如 Kafka 等 MQ 方式,異地存儲到緩存或其它數據源中,如 Redis、Elasticsearch 等。數據異構現在被普通使用,實現數據聚合,形成數據閉環。
在進行數據異構的過程中,需要關注幾個關鍵點,一是 CAP 原則,即強一致性往往被舍棄,而采用最終一致性的設計。二是緩存,緩存在微服務架構中,不能被當成銀彈來使用,使用緩存必須正視的問題有:熱點緩存 & 大 Value 緩存、緩存穿透等問題。三是消息,消息現在還存在的問題有:延遲問題、消息的不穩定性(如丟消息)、消息的不確定性(如 timeout)、消息補償、柔性事務等問題。而這些是微服務高可用架構實踐中,不能不面對的問題。
5,服務隔離
隔離也是服務拆分的一種,為什么單獨講?因為服務拆分、服務化、服務治理和服務高可用都是在事前或事中可以做的,然而,有些系統已經成了某個樣子,那么我們接收時,首先能做到的就是迅速對系統進行隔離,保證業務之間不互相影響,具體實踐包括:進程線程隔離、集群/機房隔離、讀寫隔離、動靜隔離、爬蟲/熱點隔離等。
從單體架構開始,到分布式架構設計、微服務架構,再到現在領域驅動設計,作為實踐微服務的架構師,思想上又有什么變化?
我覺得,程序員都是很悶騷的,但每個程序員心里都住一個大俠,金庸《笑傲江湖》里有氣宗和劍宗之分,什么是劍宗,我理解就像是劍招、招數,如 spring、spring boot、spring cloud,還有 tomcat、netty、serviceless 等,這些技術、中間件、框架等就像腳手架一樣可以幫助我們提高效率,但是,我們是否需要出現一個新技術就學習一個技術呢?而這就讓我想到了氣宗,它就像是通過對內在規律的掌握,打通任督二脈,實現融會貫通,正如《九陽神功》所說:他強任他強,清風拂山崗,我自一口真氣足。如 Kakfa、Flink、Hbase 等分區可用性保障的設計思想都是基于 BigTable 的分區復制策略。
正是重心由外到內的轉變,讓我認知到,現在系統架構的發展,很多系統架構的復雜度,已經不是簡簡單單通過引入一些新技術或框架,就能降低系統復雜的。它需要大大的提前對風險、問題、隱患的預知,做到未雨綢繆。《從零開始學架構》書中提到:架構設計的發展歷程就是在于降低軟件系統復雜的歷程。
《架構整潔之道》書中提到:一個軟件系統存在的意義,是系統用來賺錢或省錢的那部分代碼,那才是整個系統的皇冠明珠。所以,當拿到一個需求的時候,首先考慮的是要解決什么問題,它的問題域是什么,而不是先考慮用哪種技術去實現或解決這個問題,這是本末倒置的處理方法。
從領域驅動設計的角度來看,需求首先是問題域,屬于業務邊界的事情,梳理了解要實現什么功能和需求,然后在外延到工作邊界,確認團隊合作的方式,因為很多需求都可能需要跨團隊合作的,最后才會到應用邊界,即技術實現的解決問題領域。
上圖是一個典型的洋蔥頭模型,它的思想就是闡述了從內到外的思考過程。然后,現實工作中,經常有人從外往內去考慮問題,從我過往的經歷舉例,一次是數據異構的方案,之前是采用 Storm,后來 flink 開始流行,我就將業務切換到了 flink,但是對于 flink 的調優不到位,從而影響了系統穩定性,還有一次是之前搜索用 solr,后來 es 開始流行,就像切到 es,但是 es 和 solr 兩套搜索引擎的搜索排序結果是不一樣的,最后,我們只能強奸了業務。其實,這都是不太對的。《架構整潔之道》也提到:良好的架構設計應該盡可能地允許用戶推遲和延后決定采用什么框架、數據庫、Web 服務以及其他與環境相關的工具。
理解領域驅動設計,需求或問題它開始于一個領域意愿,由領域專家和交付團隊一起,輸出統一語言和領域知識,統一語言可以是敏捷看板,因為一個團隊是由產品、運營、研發、測試組成的,彼此之間是基于統一語言進行溝通的,然后定義領域邊界,區分核心領域、普通領域、支持領域,其中核心領域就是由核心團隊開發的,支持領域可以是外包等實現的,在通過映射上下文,確認限界上下文,最終確認系統應用邊界。
(圖片來自網絡)采用領域驅動設計的六邊形架構繪制了一張交易系統的領域邊界圖,不同顏色表明不同領域,U/D 代表上游和下游。這個圖還不算完整,因為領域業務邏輯是要為端服務的,而通過對領域的梳理,可以通用的支持很多端。即表達為領域有界,端無界。最終,每個限界上下文即是微服務。
?
其實,每個系統的微服務架構演進道路各不相同,所以,實踐微服務不能一板一眼原封照抄,而是要根據自己的業務特征,合理的裁剪,找到合適落地方案。總結一句話,那就是:因需而變。雖然微服務的路徑是不一樣的,但方向是相同的。再次回顧微服務的架構演進歷程,你會發現,從單體架構開始,進行拆分,形成微服務之后,又因為各種這樣的原因,很多微服務架構又在進行合并,現在領域驅動設計來了,又開始重新拆分。真可謂:話說天下大勢,分久必合,合久必分。
?
---------------
作者:張松然,京東商城商家研發部架構師。豐富的構建高性能高可用大規模分布式系統的研發、架構經驗。2013 年加入京東,目前負責京麥服務網關的系統研發工作。作者公眾號如下:
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的微服务架构与领域驱动设计应用实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字节跳动面试官问我看过哪些源码,然后就没
- 下一篇: Redis序列化、RedisTempla