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



長(zhǎng)按訂閱更多精彩▼

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝