文章出處:架構(gòu)之美
架構(gòu)定義是一門技術(shù),但更是一門藝術(shù)。微服務(wù)架構(gòu)是基于分而治之的思想演化出來的。過去傳統(tǒng)的一個大型而又全面的系統(tǒng),隨著互聯(lián)網(wǎng)的發(fā)展已經(jīng)很難滿足市場對技術(shù)的需求,于是我們從單獨架構(gòu)發(fā)展到分布式架構(gòu)。
微服務(wù)架構(gòu)是一種架構(gòu)模式,它提倡將單一應用程序劃分成一組小的服務(wù),服務(wù)之間互相協(xié)調(diào)、互相配合,為用戶提供最終價值。
關(guān)于微服務(wù)架構(gòu)設(shè)計呢?簡單來說可分為下面三個步驟:
第一步,把應用中關(guān)鍵的需求定義出來;
第二步,識別出采用微服務(wù)架構(gòu)時應用中所包含的所有服務(wù);
第三步,將第一步所定義出的關(guān)鍵需求作為架構(gòu)需求的場景來描述服務(wù)之間如何進行協(xié)作。這個步驟很像單體架構(gòu)下我們所做的系統(tǒng)高層架構(gòu)設(shè)計,通過高層架構(gòu)設(shè)計會識別并定義出各個業(yè)務(wù)領(lǐng)域模型,這些業(yè)務(wù)領(lǐng)域模型包含了業(yè)務(wù)對象的關(guān)鍵操作流程,通過這些業(yè)務(wù)領(lǐng)域模型就可以輔助我們規(guī)劃出整個應用架構(gòu),即各模塊之間的協(xié)作關(guān)系。
在識別應用中的服務(wù)時,應首先專注于業(yè)務(wù),通過業(yè)務(wù)邏輯的視角可以快速有效地將核心微服務(wù)識別出來。核心微服務(wù)識別出來之后,就可以圍繞核心服務(wù)把相關(guān)聯(lián)的服務(wù)都定義出來,并可以對這些服務(wù)進行分組、合并處理,最終完整定義出應用的一系列微服務(wù)。切記,不可以一開始從技術(shù)的角度去拆分,否則由于業(yè)務(wù)之間的關(guān)聯(lián)關(guān)系很有可能會將設(shè)計出來的微服務(wù)拉入“焦油坑”中。
當識別出應用的每一個微服務(wù)后,我們就需要考慮這些微服務(wù)之間如何進行協(xié)作。定義各個微服務(wù)之間協(xié)作關(guān)系最有效的方式就是根據(jù)每一個業(yè)務(wù)進行分析。
有些業(yè)務(wù)場景可能只需要某一個服務(wù)就可以完成,有些業(yè)務(wù)場景則可能需要兩個或多個服務(wù)才可以。這些協(xié)作可能是實時同步的,也可能是異步執(zhí)行,我們可以根據(jù)這些具體需求來確定使用何種方式進行交互(是使用REST、RPC,還是消息)。此外,還有一個需要我們第一時間去考慮的問題就是用戶的服務(wù)請求最初是由哪個服務(wù)承擔的。
可能我們在接觸微服務(wù)架構(gòu)之初,一腔熱血看哪個都可以作為一個微服務(wù),最終將系統(tǒng)中的每一個部分都拆分成了一個微服務(wù)。但如果是這樣,我們所設(shè)計的微服務(wù)粒度將太過細,每一個微服務(wù)可能只實現(xiàn)了數(shù)據(jù)處理,而業(yè)務(wù)處理需要粘合過多的代碼才能夠讓這些微服務(wù)整合來完成一個具體的業(yè)務(wù)處理,反而造成了更加復雜的系統(tǒng),明顯不合理。
對于設(shè)計微服務(wù)來說,最好的方式是先專注于各個服務(wù)之間的交互,先把它們劃分成粗顆粒度的服務(wù),然后隨著系統(tǒng)的升級和功能的提升,再將這些粗顆粒度的服務(wù)逐漸細化,形成更為合理的微服務(wù)粒度。
隨著應用功能需求的增加或變化,原來的一個微服務(wù)中所承擔的責任也在增加,當我們發(fā)現(xiàn)在一個微服務(wù)中已經(jīng)承擔了多種職責的話,這個時候就是考慮對微服務(wù)進行拆分的最佳時機。
那么如何衡量我們所設(shè)計的微服務(wù)粒度是否合適呢?對于過于粗粒度的微服務(wù)來說,該微服務(wù)一定承擔了太多的職責,往往在服務(wù)中塞了過多的業(yè)務(wù)邏輯和業(yè)務(wù)規(guī)則,而且業(yè)務(wù)流程也非常復雜,難以理解(常常會讓你感覺好像還是在開發(fā)單體系統(tǒng)一樣)。對于粗粒度的微服務(wù)另一個明顯的表現(xiàn)就是擁有眾多數(shù)據(jù)的管理權(quán)限。
對于一個粒度合適的微服務(wù)來說,其所管轄的數(shù)據(jù)也是有限的,一旦某個微服務(wù)所管轄的數(shù)據(jù)眾多,并且這些數(shù)據(jù)之間也沒有合適的業(yè)務(wù)關(guān)聯(lián),那么顯然該微服務(wù)粒度太粗了,需要進行細化。
反之,如果所設(shè)計的微服務(wù)顆粒度太細,一個明顯的標志就是每一個微服務(wù)幾乎都需要和其他的微服務(wù)進行溝通,每個微服務(wù)只承擔其中很少量的業(yè)務(wù)處理,然后就交給其他微服務(wù)處理,造成了一個外部請求需要經(jīng)過太多的微服務(wù)才能夠完成處理。當你想單拎出一個服務(wù)時,發(fā)現(xiàn)幾乎不可能,因為每一個微服務(wù)都依賴于其他微服務(wù),同時又被其他微服務(wù)所依賴。
微服務(wù)架構(gòu)的設(shè)計一定是與時俱進的,因此我們也不可能在第一次設(shè)計時就設(shè)計出一個完美的架構(gòu)體系。因此,在最初構(gòu)建粗顆粒度的服務(wù)要優(yōu)于過細的微服務(wù),因為粗粒度的微服務(wù)會隨著系統(tǒng)升級而逐漸細化形成粒度合適的微服務(wù),而過細的微服務(wù)在構(gòu)建和管理上非常復雜,也難以重構(gòu)、合并成合適的大小。
此外,也不要太過糾結(jié)教條式的設(shè)計規(guī)約,在開始時甚至可以允許兩個微服務(wù)之間的數(shù)據(jù)進行相互處理和聚合,因為對于許多業(yè)務(wù)對象之間畢竟并沒有一個清晰的界限。還是那句話:實踐出真知,只有你行動了,開始著手讓微服務(wù)“跑”起來了,終究有一天會找到那個合適你的微服務(wù)架構(gòu)方案,否則即使討論百遍也得不到。
微服務(wù)架構(gòu)的開發(fā)尚處于“蠻荒”時代,并沒有一些成型的指導原則和模式供我們參考,但是我們可以從面向?qū)ο蟮拈_發(fā)理論中進行借鑒。Robert C.Martin在《敏捷軟件開發(fā):原則、模式與實踐》一書中提出了面向?qū)ο箝_發(fā)的一系列原則與模式,其中有以下兩個原則可以在微服務(wù)拆分的時候借鑒:
單一職責原則(Single Responsibility Principle,SRP):一個類應該有且只有一個變化的原因。
There?should?never?be?more?than?one?reason?for?a?class?to?change
我們在開發(fā)的時候深有體會,每一個職責都是一個變化引起類變化的中心。當功能變化時,通常需要通過更改相關(guān)的類來實現(xiàn)。如果一個類擁有多個職責,那么就會有多于一個原因來導致這個類的變化。另外,一個類承擔多個職責后,往往這些職責就會耦合在一起,某一職責的改變可能會影響到其他的職責。這樣的類設(shè)計是非常脆弱的,從而會導致應用的穩(wěn)定性。因此,我們在進行類設(shè)計時要遵守單一職責原則。
同樣,對于微服務(wù)設(shè)計來說,如果一個微服務(wù)承擔太多職責的話,也會導致微服務(wù)業(yè)務(wù)之間的耦合,為業(yè)務(wù)進行改變時埋下了不穩(wěn)定因素。所以,單一職責原則同樣也適用于微服務(wù)設(shè)計,我們可以將微服務(wù)保持足夠小,僅擁有一個業(yè)務(wù)職責,保持微服務(wù)的業(yè)務(wù)單一性,從而提升應用的穩(wěn)定性。
共同封閉原則(Common Closure Principle,CCP):
包中的所有的類對于同一種性質(zhì)的變化應該是共同封閉的。一個變化若對一個封閉的包產(chǎn)生影響,則將對該包中的所有類產(chǎn)生影響,而對其他包則不造成任何影響。
The class in package should be closed together against the same kinds of changes. A change the affects a package affects all the classes in that package
簡單來說,共同封閉原則是延伸了面向?qū)ο箝_發(fā)中六大原則之一的開閉原則(OCP)中的關(guān)閉概念。就是說當需要修改某項業(yè)務(wù)時,我們需要將修改的范圍限制在同一個包內(nèi),而不是遍布在很多包中。共同封閉原則指導我們?nèi)绾螌︻愡M行有效的組織,將那些在業(yè)務(wù)概念上聯(lián)系得非常緊密、通常一起發(fā)生改變的類,封裝到同一個包中。通過共同封閉原則可以提升對應用組織上的管理。
同樣,通過使用共同封閉原則可以將那些在業(yè)務(wù)上聯(lián)系緊密,由于同一個原因而改變的服務(wù)組織在一個微服務(wù)中。這樣一方面我們可以減少微服務(wù)的數(shù)量,另外一方面當業(yè)務(wù)發(fā)生改變時我們只需要一個業(yè)務(wù)開發(fā)團隊進行單獨修改,只需要重新部署該服務(wù)即可,減少了不同微服務(wù)開發(fā)團隊之間溝通成本。
一個團隊越大,那么溝通與協(xié)助成本就會越高。因此,在微服務(wù)治理中有一個重要的理念就是自治,自治范圍并不只是代碼和數(shù)據(jù),還包含微服務(wù)的運行和維護管理,所以亞馬遜的微服務(wù)有一個規(guī)則:你構(gòu)建,你運行。
將微服務(wù)分而治之的另一個重要方面是數(shù)據(jù)管理的分而治之。傳統(tǒng)單體架構(gòu)應用的開發(fā)在很多時候多個業(yè)務(wù)之間的數(shù)據(jù)交互是直接通過操作數(shù)據(jù)庫來完成,當需要更改某一業(yè)務(wù)數(shù)據(jù)庫表時往往會涉及多個模塊,甚至有時候根本不清楚修改這張數(shù)據(jù)庫表到底會影響到多少業(yè)務(wù)代碼,從而不敢動數(shù)據(jù)庫表的定義,只好退而求其次,通過增加表來處理,進而加劇了系統(tǒng)架構(gòu)的惡化。
雖然現(xiàn)在O/R mapping技術(shù)的出現(xiàn)從一定程度上解決了這個頭痛的問題,但終未從根本上解決。而微服務(wù)中的分而治之理念,不但是指業(yè)務(wù)功能,也同時包含了對業(yè)務(wù)數(shù)據(jù)的管理。將業(yè)務(wù)數(shù)據(jù)管理進行私有化之后就進一步降低了業(yè)務(wù)之間的耦合度,所以實施微服務(wù)的架構(gòu)師,一定要保持業(yè)務(wù)數(shù)據(jù)管理的私有化,即使你在項目中不能夠分庫,也要牢記這條規(guī)則,嚴格要求各微服務(wù)團隊看好自己的數(shù)據(jù)。
微服務(wù)架構(gòu)中的數(shù)據(jù)自治是指每個微服務(wù)擁有其業(yè)務(wù)領(lǐng)域?qū)ο笙碌臄?shù)據(jù),只有該微服務(wù)可以對這些數(shù)據(jù)進行操作(包含讀取與更改),而其他微服務(wù)只有通過該服務(wù)才能訪問到這些數(shù)據(jù),不能直接通過數(shù)據(jù)庫進行溝通。因此,我們可以不用為每一個微服務(wù)創(chuàng)建一個獨立數(shù)據(jù)庫,可以將它們統(tǒng)一存放在一個數(shù)據(jù)庫中,保障不破壞上述的數(shù)據(jù)訪問原則即可。
當我們開始使用微服務(wù)架構(gòu)進行開發(fā)時,一個清晰明了、規(guī)范的交互方式將極大提升應用開發(fā)效率。通常,我們可以使用以下原則作為微服務(wù)接口設(shè)計的準則。
-
使用REST協(xié)議:REST可以說在微服務(wù)互相調(diào)用之間起著非常重要的角色,強烈建議大家使用HTTP作為服務(wù)的調(diào)用協(xié)議,并在服務(wù)處理上使用HTTP標準動詞(GET、PUT、POST和DELETE)。
-
使用URI表達:服務(wù)端點的URI應該能夠清晰表達出我們所要解決的問題、提供的方法、相應資源信息及資源之間的關(guān)聯(lián)關(guān)系。
-
使用JSON數(shù)據(jù)格式:JSON作為輕量級數(shù)據(jù)格式協(xié)議,及自帶的序列化和反序列化機制,幾乎已經(jīng)成為通信中的數(shù)據(jù)標準協(xié)議,并且對于前端開發(fā)來說非常容易使用與整合。
-
使用HTTP標準狀態(tài)碼:HTTP協(xié)議本身具有非常豐富的狀態(tài)碼,那么使用這些狀態(tài)碼來作為服務(wù)調(diào)用結(jié)果的狀態(tài)是非常合適的。以上準則,總結(jié)起來就是讓所設(shè)計的微服務(wù)接口更清晰,更容易讓其他開發(fā)者掌握和使用。
-? ? ?微服務(wù)架構(gòu)遷移? ? ?-
將單體架構(gòu)應用遷移到微服務(wù)架構(gòu)意味著一個漫長的過程,不過這和在開發(fā)時經(jīng)常做的代碼重構(gòu)類似,只是變成了對架構(gòu)的重構(gòu),因此可以從中吸取一些思路。對于代碼重構(gòu),有一個很重要的指導思想就是不要大規(guī)模進行重構(gòu),而是一小步一小步來。作為開發(fā)人員,每次聽到重寫代碼可能會很興奮,但實際上卻是充滿了風險,道路也是非常崎嶇坎坷,最終也有可能會失敗,每一個重寫過代碼的開發(fā)者可能對這一點深有體會。
因此,與大規(guī)模進行重構(gòu)相反,在進行微服務(wù)架構(gòu)遷移時可以使用Martin Fowler提出絞殺(Strangler)模式。該策略名字來源于雨林中的絞殺藤,絞殺藤為了能夠爬到森林頂端都要纏繞著某棵大樹生長,最終使被纏繞的大樹死掉,只留下樹形一樣的絞殺藤。通過這種策略,我們在遷移時應首先圍繞著傳統(tǒng)應用開發(fā)出新的微服務(wù)應用,并逐漸替代傳統(tǒng)應用中的部分業(yè)務(wù)功能。通過這種方式逐步構(gòu)建微服務(wù)應用,并替代、兼容整合舊的傳統(tǒng)應用,直到微服務(wù)承擔全部應用功能,而傳統(tǒng)單體架構(gòu)應用此時也就可以退出歷史舞臺了。
特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝