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