微服務(wù)架構(gòu)核心:專注執(zhí)行同一件事并做好
微服務(wù)有且僅有一種非常專項(xiàng)的功能,通過遠(yuǎn)程API來提供系統(tǒng)其余功能。舉個(gè)例子:試想一下倉(cāng)庫(kù)的管理系統(tǒng),這樣的系統(tǒng)中微服務(wù)可能提供的一些功能有:
- 接收庫(kù)存
- 計(jì)算新的庫(kù)存該存到什么地方
- 計(jì)算在倉(cāng)庫(kù)內(nèi)將庫(kù)存運(yùn)往正確放置點(diǎn)的路線
- 為倉(cāng)庫(kù)員工分配運(yùn)送路線
- 接收訂單
- 計(jì)算倉(cāng)庫(kù)內(nèi)指定一組訂單的揀貨路線
- 為倉(cāng)庫(kù)員工分配揀貨路線
以上這些功能(可能還會(huì)有更多)都是由單個(gè)微服務(wù)實(shí)現(xiàn)的。每個(gè)微服務(wù)都有單獨(dú)的運(yùn)行線程,并且可以獨(dú)立于其他微服務(wù)進(jìn)行部署。同樣每個(gè)微服務(wù)都有自己的專用數(shù)據(jù)庫(kù),盡管每個(gè)微服務(wù)都會(huì)與其他微服務(wù)協(xié)作與溝通。
一個(gè)系統(tǒng)中的不同微服務(wù)完全有可能在不同的平臺(tái)上實(shí)現(xiàn),一些可能在.NET上,另外一些在Erlang,其他的在Node.js上。只要能協(xié)調(diào)多語言的問題,各個(gè)微服務(wù)彼此正常溝通,就能奏效。HTTP是良好的溝通選擇:上面所有提到的平臺(tái),還有很多其他平臺(tái)都能很好的處理HTTP。當(dāng)然也有符合微服務(wù)溝通規(guī)則的其他技術(shù):例如一些隊(duì)列、一些服務(wù)總線還有一些二進(jìn)制協(xié)議。在這些技術(shù)當(dāng)中,HTTP可能是支持最廣泛的,相當(dāng)容易理解,而且就像萬維網(wǎng)所展示的那樣很好用,總體來說是很好的方案。
再次以倉(cāng)庫(kù)系統(tǒng)為例:該系統(tǒng)的一個(gè)微服務(wù)是分配揀貨路線微服務(wù)。圖一展示了“分配揀貨路線微服務(wù)”從另一個(gè)協(xié)作微服務(wù)收到的請(qǐng)求:為指定員工設(shè)定了下一次的揀貨路線。分配揀貨線路微服務(wù)必須為員工找到合適的線路,而另一個(gè)微服務(wù)則完成計(jì)算***路線的工作,分配揀貨路線微服務(wù)只需收到揀貨路線通知并確定如何為雇員分配路線。在分配揀貨路線的微服務(wù)中,收到請(qǐng)求——分配指定員工的揀貨路線,搜索數(shù)據(jù)庫(kù),找到合適的揀貨路線,并從中選擇一個(gè)返回給微服務(wù)調(diào)用。
圖一 分配揀貨路線微服務(wù)
微服務(wù)架構(gòu)是什么?
微服務(wù)是一種架構(gòu)類型,屬于輕量級(jí)的面向服務(wù)體系架構(gòu),這些服務(wù)都是嚴(yán)格專注于執(zhí)行同一件事并把它做好。
使用微服務(wù)作為主要架構(gòu)類型的系統(tǒng)是一個(gè)擁有大量協(xié)調(diào)微服務(wù)的分布式系統(tǒng),每個(gè)微服務(wù)分管自己的進(jìn)程。由于微服務(wù)之間緊密協(xié)作,每個(gè)微服務(wù)只提供拼圖的一小塊,而系統(tǒng)做為完整的作品存在。協(xié)作時(shí),各服務(wù)彼此通過一個(gè)不綁定具體平臺(tái)的輕量級(jí)媒介進(jìn)行溝通,比如.NET,Java或者Erlang。如前所述,本書中所有微服務(wù)之間的溝通都是通過HTTP的,不過還有其他可選方案,比如隊(duì)列、總線或者類似Thrift的二進(jìn)制協(xié)議。
在構(gòu)建與維護(hù)復(fù)雜的服務(wù)器端軟件系統(tǒng)時(shí),微服務(wù)架構(gòu)類型迅速流行起來。可以想見,這樣一來:在傳統(tǒng)的面向服務(wù)方法和整體架構(gòu)(monolithic architectures)中,微服務(wù)都有大量潛在好處。在運(yùn)作良好的前提下,微服務(wù)在可塑性、可擴(kuò)展性與彈性方面都具有優(yōu)勢(shì),并允許使用者只花費(fèi)很短的時(shí)間就實(shí)現(xiàn)從開始到生產(chǎn)環(huán)境部署的過程。
微服務(wù)特性
雖然已經(jīng)說了這么多,不過定義還很模糊。為了縮小微服務(wù)的界定范圍,我們先來考察一下微服務(wù)的特性。在筆者理解中,微服務(wù)這個(gè)術(shù)語的特性是:
1. 負(fù)責(zé)單個(gè)功能
2. 單獨(dú)部署
3. 包含一個(gè)或多個(gè)進(jìn)程
4. 擁有自己的數(shù)據(jù)存儲(chǔ)
5. 一支小團(tuán)隊(duì)就能維護(hù)幾個(gè)微服務(wù)
6. 可替換的
這張?zhí)匦粤斜聿坏珟椭R(shí)別微服務(wù),還能夠在發(fā)揮微服務(wù)優(yōu)勢(shì)(一個(gè)擁有可塑性、可擴(kuò)展性與彈性的系統(tǒng))的前提下協(xié)助界定與執(zhí)行該服務(wù),依次看下去。
負(fù)責(zé)單個(gè)功能
微服務(wù)在整個(gè)系統(tǒng)中只負(fù)責(zé)單個(gè)功能。這句話分解來說包含兩部分內(nèi)容:***,微服務(wù)只有單個(gè)責(zé)任;第二,負(fù)責(zé)的是功能。單一責(zé)任原則有幾種描述,其中一個(gè)傳統(tǒng)的描述是:
“當(dāng)需要修改某個(gè)類的時(shí)候原因有且只有一個(gè)("There should never be more than one reason for a class to change.")” -- Robert C. Martin SRP: 單一責(zé)任原則
盡管這種說法特別提到了“類”,這一原則卻不只適用于面向?qū)ο笳Z言的類層面。通過微服務(wù),這里在服務(wù)層面運(yùn)用單一責(zé)任原則。另一種較新的說法也是描述單一責(zé)任原則的:
聚合因同一理由變化的東西,分離因不同理由而變化的東西。("Gather together the things that change for the same reasons. Separate those things that change for different reasons.")-- Robert C. Martin單一責(zé)任原則
這一原則適用于微服務(wù):微服務(wù)應(yīng)當(dāng)正好實(shí)現(xiàn)一個(gè)功能。微服務(wù)必須只在功能改變時(shí)才跟著改變。此外,應(yīng)當(dāng)努力讓微服務(wù)完全實(shí)現(xiàn)相關(guān)功能,這樣在功能改變時(shí)微服務(wù)也得跟著改變。
微服務(wù)系統(tǒng)的一個(gè)功能可能意味著幾件事。首先,功能可能是業(yè)務(wù)方面的。業(yè)務(wù)功能就是系統(tǒng)所完成的、對(duì)系統(tǒng)的目的有貢獻(xiàn)的事情——比如持續(xù)追蹤用戶的購(gòu)物車或者計(jì)算價(jià)格。梳理一個(gè)系統(tǒng)擁有的獨(dú)立業(yè)務(wù)功能有一個(gè)好辦法,就是使用Domain Driven Design。第二,有時(shí)候功能可以是多個(gè)其他微服務(wù)需要利用的技術(shù)功能——例如集成到一些第三方系統(tǒng)中。技術(shù)功能并非是將系統(tǒng)分解成微服務(wù)的主因,而是由于微服務(wù)執(zhí)行業(yè)務(wù)功能需要同樣的技術(shù)能力而導(dǎo)致的結(jié)果。
獨(dú)立部署
每個(gè)微服務(wù)都應(yīng)當(dāng)是單獨(dú)部署的。也就是說:當(dāng)你改變一個(gè)特定的微服務(wù)時(shí),需要能夠?qū)⑽⒎?wù)的變更部署到生產(chǎn)環(huán)境中,而無需部署或觸及系統(tǒng)的其他部分。事實(shí)上,系統(tǒng)中的其他微服務(wù)應(yīng)當(dāng)在改動(dòng)的微服務(wù)部署之時(shí),還有新版本部署完成之后繼續(xù)持續(xù)運(yùn)行。
試想一下電子商務(wù)網(wǎng)站:每次購(gòu)物車微服務(wù)發(fā)生改變時(shí),都應(yīng)當(dāng)能立即進(jìn)行部署。同時(shí)價(jià)格計(jì)算微服務(wù)、推薦微服務(wù)、產(chǎn)品目錄微服務(wù)等等應(yīng)當(dāng)繼續(xù)運(yùn)行并滿足用戶的請(qǐng)求。
能夠單獨(dú)部署每個(gè)微服務(wù)非常重要,原因有好幾個(gè)。其中一點(diǎn)是,在一個(gè)微服務(wù)系統(tǒng)中有很多微服務(wù),每個(gè)微服務(wù)都會(huì)與其他幾個(gè)相協(xié)作。各部分的開發(fā)工作同時(shí)完成,或者很多微服務(wù)并行。如果需要按同一步調(diào)部署所有或者很多微服務(wù)的話,管理部署很快就會(huì)變得捉襟見肘,特別是經(jīng)常會(huì)導(dǎo)致高風(fēng)險(xiǎn)部署,這是我們很希望避免的。相反我們希望能夠?qū)γ總€(gè)微服務(wù)進(jìn)行小變更部署,這樣風(fēng)險(xiǎn)會(huì)更低。
能夠在系統(tǒng)的其他部分繼續(xù)正常運(yùn)行的時(shí)候部署單個(gè)微服務(wù),構(gòu)建過程必須牢記這一點(diǎn):每個(gè)微服務(wù)必須打包到不同的構(gòu)件或程序包中。同樣地,部署過程本身還必須支持在其他微服務(wù)繼續(xù)運(yùn)行之時(shí),獨(dú)立部署變更的微服務(wù)。比如,每次將微服務(wù)部署到服務(wù)器的過程中,為了減少停機(jī)時(shí)間可以使用滾動(dòng)部署的辦法。
微服務(wù)互動(dòng)的方式也受到期望獨(dú)立部署的影響。改變微服務(wù)接口必須在大多數(shù)情況下向后兼容,這樣其他現(xiàn)有的微服務(wù)就可以繼續(xù)按照與舊版本融合的方式與新版本集成了。此外,微服務(wù)互動(dòng)的方式必須有彈性,每個(gè)微服務(wù)必須在其他微服務(wù)偶爾出錯(cuò)時(shí)繼續(xù)保持***運(yùn)行狀態(tài)。一個(gè)微服務(wù)出錯(cuò)——比如因?yàn)椴渴饡r(shí)的短暫停機(jī)——必須不影響其他微服務(wù)運(yùn)行,只是造成功能縮減或者進(jìn)行時(shí)間稍長(zhǎng)。
包含一個(gè)或多個(gè)進(jìn)程
一個(gè)微服務(wù)由一個(gè)或多個(gè)進(jìn)程組成,這個(gè)特性有兩面性。首先,每個(gè)微服務(wù)獨(dú)立于其他微服務(wù)運(yùn)行;其次,每個(gè)微服務(wù)可以擁有不止一個(gè)進(jìn)程。
某微服務(wù)獨(dú)立運(yùn)行,是由于希望保持每個(gè)微服務(wù)盡可能獨(dú)立于其他微服務(wù)繼續(xù)運(yùn)行。此外,為了獨(dú)立部署微服務(wù),那個(gè)微服務(wù)不能按照其他微服務(wù)的方式來運(yùn)行。再用購(gòu)物車微服務(wù)來舉例:如果按照與產(chǎn)品目錄微服務(wù)相同的方式運(yùn)行,購(gòu)物車代碼可能對(duì)產(chǎn)品目錄代碼產(chǎn)生負(fù)面影響,這代表著購(gòu)物車微服務(wù)與產(chǎn)品目錄微服務(wù)之間緊密卻不受歡迎的耦合。
現(xiàn)在思考一下部署購(gòu)物車微服務(wù)的新版本情況。要么得重新部署產(chǎn)品目錄微服務(wù),要么就得有某種動(dòng)態(tài)代碼加載功能,來替換正在運(yùn)行中的購(gòu)物車代碼。前一個(gè)選項(xiàng)與微服務(wù)獨(dú)立部署的原則完全相違背,后一個(gè)選項(xiàng)太過復(fù)雜而且起碼有由于部署購(gòu)物車微服務(wù)而造成產(chǎn)品目錄微服務(wù)停機(jī)的風(fēng)險(xiǎn)。
每個(gè)微服務(wù)可能包含不止一個(gè)進(jìn)程,表面來看可能令人驚訝,畢竟這里嘗試讓每個(gè)微服務(wù)盡可能簡(jiǎn)單好控制,那么為什么要自找麻煩擁有不止一個(gè)進(jìn)程呢?用電子商務(wù)網(wǎng)站做個(gè)比方:執(zhí)行推薦算法會(huì)在電子商務(wù)網(wǎng)站上展示推薦選項(xiàng),這些算法都在這個(gè)微服務(wù)所屬的進(jìn)程中運(yùn)行,還存儲(chǔ)了提供推薦需要的數(shù)據(jù)。這個(gè)數(shù)據(jù)可能存儲(chǔ)在硬盤文件里,不過更有可能存在數(shù)據(jù)庫(kù)里,在第二個(gè)進(jìn)程中運(yùn)行的數(shù)據(jù)庫(kù)也屬于這個(gè)微服務(wù)。一個(gè)微服務(wù)通常擁有2個(gè)或以上進(jìn)程的需求,就是因?yàn)槲⒎?wù)需要實(shí)現(xiàn)所需要的一切,以提供包含諸如數(shù)據(jù)存儲(chǔ)還有后臺(tái)處理之類的功能。
擁有自己的數(shù)據(jù)存儲(chǔ)
一個(gè)微服務(wù)包含數(shù)據(jù)存儲(chǔ),在該進(jìn)程中存儲(chǔ)所需的數(shù)據(jù),正是由于我們希望微服務(wù)的范圍是一個(gè)完整的功能。大多數(shù)業(yè)務(wù)功能需要一些數(shù)據(jù)存儲(chǔ),例如對(duì)于產(chǎn)品目錄微服務(wù)來說,每個(gè)產(chǎn)品的信息需要存儲(chǔ)下來。為了保持產(chǎn)品目錄微服務(wù)與其他微服務(wù)的松散耦合性,存儲(chǔ)的產(chǎn)品信息數(shù)據(jù)完全包含在產(chǎn)品目錄微服務(wù)之中。由產(chǎn)品目錄微服務(wù)確定何時(shí)、如何存儲(chǔ)產(chǎn)品信息。其他微服務(wù)——比如購(gòu)物車微服務(wù)——只能通過產(chǎn)品目錄微服務(wù)的接口來訪問產(chǎn)品信息,而永遠(yuǎn)不能直接訪問產(chǎn)品目錄存儲(chǔ)。
每個(gè)微服務(wù)包含自己的數(shù)據(jù)存儲(chǔ),這開啟了根據(jù)每個(gè)微服務(wù)需求,為不同微服務(wù)使用不同數(shù)據(jù)庫(kù)技術(shù)的可能性。產(chǎn)品目錄微服務(wù)可能使用SQL服務(wù)器來存儲(chǔ)產(chǎn)品信息,而購(gòu)物車微服務(wù)可能用Redis來存儲(chǔ)每個(gè)用戶的購(gòu)物車信息,推薦微服務(wù)則使用Elastic Search索引來提供推薦服務(wù)。為每個(gè)微服務(wù)所選擇的數(shù)據(jù)庫(kù)技術(shù)是執(zhí)行的一部分,對(duì)其他微服務(wù)來講是隱藏的。將數(shù)據(jù)庫(kù)技術(shù)與每個(gè)微服務(wù)需求進(jìn)行混合配對(duì)的好處在于,每個(gè)微服務(wù)可以使用最適合的數(shù)據(jù)庫(kù)。對(duì)開發(fā)時(shí)間、性能和可擴(kuò)展性很有好處,不過也帶來了成本問題。數(shù)據(jù)庫(kù)技術(shù)上非常復(fù)雜,學(xué)習(xí)使用和在生產(chǎn)環(huán)境上運(yùn)行一個(gè)可靠的數(shù)據(jù)庫(kù)都不容易。為微服務(wù)選擇數(shù)據(jù)庫(kù)信息時(shí),應(yīng)當(dāng)考慮取舍的問題。不過也要記住,由于微服務(wù)擁有自己的數(shù)據(jù)存儲(chǔ),稍候切換到另一個(gè)數(shù)據(jù)庫(kù)也是可行的。
小團(tuán)隊(duì)就能維護(hù)
到現(xiàn)在本文并未討論太多微服務(wù)的規(guī)模問題,雖然微服務(wù)中的“微”暗示著這些服務(wù)規(guī)模很小。但這里并不認(rèn)為討論微服務(wù)應(yīng)當(dāng)有幾行代碼,需求/用例有多少或者應(yīng)當(dāng)執(zhí)行的功能點(diǎn)有幾個(gè)這些有什么意義。所有這些取決于微服務(wù)所提供功能的復(fù)雜性。真正有意義的是考慮維護(hù)微服務(wù)的工作量。指出微服務(wù)規(guī)模大小的一條經(jīng)驗(yàn)法則是:一個(gè)5人小團(tuán)隊(duì)就應(yīng)當(dāng)能夠維護(hù)幾個(gè)或者更多的微服務(wù)。維護(hù)一個(gè)微服務(wù)包括保持其正常運(yùn)行并達(dá)成目標(biāo):開發(fā)新的功能、從發(fā)展到過大規(guī)模的微服務(wù)中分解出新的微服務(wù)、監(jiān)控測(cè)試與修復(fù)bug及其他。考慮到一個(gè)小團(tuán)隊(duì)?wèi)?yīng)當(dāng)能夠完成幾個(gè)微服務(wù)的所有這些工作,你應(yīng)當(dāng)對(duì)典型的微服務(wù)規(guī)模有概念了。
可替換的
一個(gè)微服務(wù)是可替換的,代表著它可以在合理的時(shí)間框架內(nèi)從頭重寫。也就是說,維護(hù)該微服務(wù)的團(tuán)隊(duì)可以決定用全新的實(shí)現(xiàn)來替代現(xiàn)有的,并且不會(huì)打亂正常工作的進(jìn)程。這條特性也是微服務(wù)規(guī)模的一條約束:如果一個(gè)微服務(wù)成長(zhǎng)地太大,替代成本就會(huì)過高,只有保持小型才能讓重寫比較現(xiàn)實(shí)。
為什么團(tuán)隊(duì)會(huì)決定重寫微服務(wù)?一個(gè)原因可能是代碼太亂,另一個(gè)原因是微服務(wù)不能在生產(chǎn)環(huán)境中運(yùn)行良好。盡管這些情況并非所愿,卻出體現(xiàn)了微服務(wù)的優(yōu)勢(shì)。即便努力構(gòu)建微服務(wù),時(shí)間造成的需求變更可能促使現(xiàn)有的實(shí)現(xiàn)方式無法滿足需求而需要變更。而且隨著時(shí)間過去,代碼可能會(huì)由于初始設(shè)計(jì)周折太多而變成一團(tuán)亂麻。性能要求可能會(huì)需要大幅提升,而現(xiàn)有設(shè)計(jì)無法滿足。如果一個(gè)微服務(wù)小到在合理時(shí)間框架內(nèi)便能重寫,偶爾出現(xiàn)這些情況都是ok的。了解現(xiàn)有實(shí)現(xiàn)所有知識(shí)的同時(shí),再結(jié)合新需求考慮,就能簡(jiǎn)單地完成重寫工作。