“微服務(wù)”的概念興起于四五年前,近幾年尤其火熱,各大廠都在進(jìn)行微服務(wù)化改造和微服務(wù)建設(shè)。最近一年來我們也參與了微服務(wù)化的改造大軍,這里寫下一些做微服務(wù)系統(tǒng)設(shè)計和開發(fā)時的切身感受。
01 微服務(wù)架構(gòu)
說起微服務(wù),不得不提那篇經(jīng)典的文章,來自Martin Flower的《Microservices》,建議多讀幾遍。Martin Flower是敏捷開發(fā)方法創(chuàng)始人之一,《重構(gòu)》《企業(yè)應(yīng)用架構(gòu)模式》作者,ThoughtWorks公司的首席科學(xué)家。微服務(wù)雖然不是在這篇文章中首次提出,但是它將發(fā)展多年的微服務(wù)架構(gòu)進(jìn)行了重要性總結(jié),推動了微服務(wù)的流行。
Martin在文章中從多個方面詳細(xì)闡述了微服務(wù)的概念,首先作者對比單體應(yīng)用的架構(gòu),一個單體應(yīng)用處理請求的所有邏輯都運(yùn)行在一個單獨(dú)的進(jìn)程中,這種情況下,你可以在筆記本上開發(fā)、測試、部署都很簡單,還可以通過負(fù)載均衡進(jìn)行橫向擴(kuò)展,最后交付給運(yùn)維團(tuán)隊。單體應(yīng)用非常成功,但是越來越多的人感覺不妥,尤其是在云中部署的時候,任何微小的變更都要整體重新構(gòu)建和部署,擴(kuò)展的時候也需要整體擴(kuò)展,不能進(jìn)行部分?jǐn)U展。這時候微服務(wù)架構(gòu)風(fēng)格就出現(xiàn)了,它提出將應(yīng)用程序構(gòu)建為一套服務(wù),每個服務(wù)都可以獨(dú)立部署和擴(kuò)展,運(yùn)行在獨(dú)立的進(jìn)程中,服務(wù)之間通過RPC調(diào)用進(jìn)行通信。微服務(wù)的應(yīng)用致力于松耦合和高內(nèi)聚:采用單獨(dú)的業(yè)務(wù)邏輯封裝,接受請求、處理業(yè)務(wù)邏輯、返回響應(yīng),而且喜歡簡單的REST風(fēng)格,而不喜歡復(fù)雜的協(xié)議,最終實現(xiàn)敏捷開發(fā)。
微服務(wù)不是什么框架,也不是什么系統(tǒng),只是一種架構(gòu)風(fēng)格。我們所使用的DSF(華為)、DUBBO(阿里)、Spring Clould(pivotal)等框架,在介紹中都稱是分布式服務(wù)框架。這些分布式服務(wù)框架都是微服務(wù)架構(gòu)必不可少的基礎(chǔ)能力,微服務(wù)一定是分布式的。分布式服務(wù)的概念比較模糊,只是解決了網(wǎng)站的高并發(fā)問題,很多細(xì)節(jié)問題是微服務(wù)幫其明確的。微服務(wù)更加強(qiáng)調(diào)敏捷和健壯,強(qiáng)調(diào)服務(wù)的粒度,一個服務(wù)只需完成一個單一的、獨(dú)立的功能,多個微服務(wù)組合完成相對復(fù)雜的業(yè)務(wù)系統(tǒng),以滿足需求。而且微服務(wù)注重借助于各種中間件進(jìn)行業(yè)務(wù)解耦和提高性能,以及提高服務(wù)的容錯性。
從上面的介紹中我們可以看出,微服務(wù)架構(gòu)有很多優(yōu)點(diǎn)。例如,服務(wù)的拆分將復(fù)雜問題簡單化;每個服務(wù)由專門的團(tuán)隊開發(fā),開發(fā)者可以自由選擇實現(xiàn)技術(shù),提供API服務(wù);每個微服務(wù)都可獨(dú)立部署,加快了部署速度;每個服務(wù)可獨(dú)立擴(kuò)展以滿足需求。微服務(wù)不是免費(fèi)的午餐,微服務(wù)架構(gòu)也有缺點(diǎn)。微服務(wù)概念強(qiáng)調(diào)了服務(wù)的大小,但是服務(wù)變小不是最終目的,微服務(wù)的目的是有效地拆分應(yīng)用,實現(xiàn)敏捷開發(fā)和部署;微服務(wù)應(yīng)用都是分布式系統(tǒng),需要通過RPC進(jìn)程間通信完成服務(wù)調(diào)用,這樣大大增加了系統(tǒng)的復(fù)雜性,因此必須寫代碼處理由于網(wǎng)絡(luò)或者服務(wù)不可用等導(dǎo)致的調(diào)用失敗問題,雖然一般框架都支持相關(guān)配置,但是在這種情況下微服務(wù)顯得相對復(fù)雜些;在微服務(wù)架構(gòu)的應(yīng)用中,建議不同的服務(wù)使用不同的數(shù)據(jù)庫,這種情況下,一個交易一般會調(diào)用多個服務(wù),同時需要修改多個數(shù)據(jù)庫,由于CAP理論和其它的一些因素,并不能實時地保證數(shù)據(jù)的一致性,因此不得不使用最終一致性的方法,從而對開發(fā)者提出了更高的要求和挑戰(zhàn);測試一個微服務(wù)架構(gòu)的應(yīng)用也是很復(fù)雜的任務(wù),首先需要啟動和它相關(guān)的所有服務(wù)(至少需要這些服務(wù)的stubs)。最后還是要強(qiáng)調(diào)一下,不要低估了微服務(wù)架構(gòu)帶來的復(fù)雜性,下面介紹一下我們?nèi)绾螒?yīng)對這樣的復(fù)雜性。
微服務(wù)架構(gòu)給我們帶來方便的同時,也引入一定的系統(tǒng)復(fù)雜性,最近幾年隨著基礎(chǔ)設(shè)施自動化技術(shù)的發(fā)展,比如云平臺、容器技術(shù),再加上自動化測試與持續(xù)集成,減少了構(gòu)建、發(fā)布、運(yùn)維微服務(wù)的復(fù)雜性。因此我們的微服務(wù)團(tuán)隊?wèi)?yīng)該依賴基礎(chǔ)設(shè)施自動化技術(shù)構(gòu)建軟件,下圖說明這種構(gòu)建的流程:
在構(gòu)建微服務(wù)軟件時使用的分布式服務(wù)框架也擁有很多特性,這些特性提供了很多復(fù)雜問題的解決方案,比如異步調(diào)用、超時失敗策略、故障隔離、健康檢查、流量控制以及自定義路由等,這些特性大大減輕了開發(fā)者處理邏輯的負(fù)擔(dān),還有一些高性能、高可用的保障都增加了我們構(gòu)建微服務(wù)的信心。而且經(jīng)過多年的微服務(wù)實踐者的摸索,也總結(jié)出了很多輔助運(yùn)維系統(tǒng),比如統(tǒng)一日志收集(ELK)、服務(wù)管理、服務(wù)監(jiān)控和服務(wù)治理等,大大減輕了運(yùn)維的工作,而且能讓我們對復(fù)雜的微服務(wù)系統(tǒng)做到心中有數(shù)。
總之,微服務(wù)架構(gòu)入坑容易,坑了呆得舒服難。各種基礎(chǔ)設(shè)施和配套系統(tǒng)必須跟得上,還得有一個具有微服務(wù)特性的團(tuán)隊,才能真正駕馭得了微服務(wù)。
兩個值得深入的話題:
1.微服務(wù)與持續(xù)集成
2.微服務(wù)與測試
02 微服務(wù)團(tuán)隊
上一節(jié)中說到更適合構(gòu)建微服務(wù)應(yīng)用的微服務(wù)團(tuán)隊,什么時微服務(wù)團(tuán)隊?說一個比較現(xiàn)實的問題,當(dāng)我們做服務(wù)拆分的時候,通常管理都會集中在技術(shù)層面,涉及到UI團(tuán)隊、服務(wù)端業(yè)務(wù)邏輯團(tuán)隊、數(shù)據(jù)庫團(tuán)隊、測試團(tuán)隊和運(yùn)維團(tuán)隊。當(dāng)采用這種標(biāo)準(zhǔn)對團(tuán)隊進(jìn)行劃分時,即使時小小的變更都將導(dǎo)致跨團(tuán)隊項目協(xié)作,從而消耗時間和預(yù)算審批。這就是 Conway's Law :
設(shè)計一個系統(tǒng)的任何組織(廣義上)都會產(chǎn)生這樣一種設(shè)計,其結(jié)構(gòu)是組織交流結(jié)構(gòu)的復(fù)制。
——Melvyn Conway, 1967
Melvyn Conway 的意思是設(shè)計一個系統(tǒng)的團(tuán)隊結(jié)構(gòu)將決定了一個軟件系統(tǒng)的結(jié)構(gòu),將人員劃分為 UI 團(tuán)隊,中間件團(tuán)隊,DBA 團(tuán)隊,那么相應(yīng)地,軟件系統(tǒng)也就會自然地被劃分為 UI 界面,中間件系統(tǒng),數(shù)據(jù)庫。而一個高效的微服務(wù)團(tuán)隊會針對這種情況進(jìn)行改善,微服務(wù)團(tuán)隊需要是一個全棧的團(tuán)隊,跨職能的團(tuán)隊,應(yīng)該包含整個項目周期的所有技能,前后端開發(fā)、UI設(shè)計、測試、構(gòu)建部署、上線與運(yùn)維都是必須技能。
微服務(wù)團(tuán)隊除了熟練的業(yè)務(wù)邏輯開發(fā)之外,還需要有DevOps能力、服務(wù)的快速構(gòu)建、良好的團(tuán)隊文化:
- 首先DevOps能力是保證持續(xù)交付和應(yīng)對復(fù)雜運(yùn)維問題的動力之源,運(yùn)維人員不懂開發(fā)人員的服務(wù)設(shè)計和調(diào)用流程,開發(fā)人員不懂產(chǎn)品環(huán)境和整體配置結(jié)構(gòu),開發(fā)和運(yùn)維的融合才能更好的應(yīng)對微服務(wù)架構(gòu)。正如下面這句話所說的,“你構(gòu)建,你運(yùn)維”。
You build it, you own it. – Amazon CTO
因此,需要打造DevOps文化,將運(yùn)維作為需求提前注入到開發(fā)流程中。
其次保持服務(wù)的持續(xù)演進(jìn),使服務(wù)能夠快速、低成本地被拆分和合并,以快速響應(yīng)業(yè)務(wù)的變化。
同時要保持團(tuán)隊和架構(gòu)的對齊,微服務(wù)看似是技術(shù)層面的變革,但它對團(tuán)隊結(jié)構(gòu)和組織文化有很強(qiáng)的要求和影響,識別和構(gòu)建匹配架構(gòu)的團(tuán)隊是解決問題的加速器。微服務(wù)團(tuán)隊的另一個關(guān)鍵點(diǎn)是持續(xù)改進(jìn)、持續(xù)學(xué)習(xí)和反饋,只有保持這樣一個文化氛圍,微服務(wù)架構(gòu)才能持續(xù)發(fā)展下去,保持新鮮的生命力,進(jìn)而實現(xiàn)我們的初衷。
專業(yè)的微服務(wù)團(tuán)隊,為微服務(wù)保駕護(hù)航。
03 項目的微服務(wù)設(shè)計實踐
我們在各種基礎(chǔ)設(shè)施不健全的情況下,匆忙進(jìn)入了微服務(wù)改造的深淵。原來的單體應(yīng)用按照功能拆分成了下面的結(jié)構(gòu):
拆分之后,模塊突然變多了。將內(nèi)部業(yè)務(wù)邏輯和原子功能拆分出來,實現(xiàn)了部分功能的復(fù)用,并且對外的接口按類型區(qū)分到不同的模塊中去了,方便了使用,但是增加了管理的難度。
從上面我們對微服務(wù)架構(gòu)的了解,一個微服務(wù)系統(tǒng)會有大量的服務(wù)組成,服務(wù)之間的層次關(guān)系依然是存在的,但是調(diào)用順序上的要求會有所降低,只要滿足嚴(yán)格的上層調(diào)用下層即可,在某些簡單的業(yè)務(wù)功能上允許跨層調(diào)用。
上圖是三種架構(gòu)(集中式、分布式、微服務(wù)架構(gòu))的形象化展示。可以看到微服務(wù)的一個狀態(tài),乍看起來有些混亂,如果還按照以前的管理方式維護(hù)項目的話,工作量會成指數(shù)級增長,人力所無法勝任的工作。
這時候就需要依賴一些分布式服務(wù)框架,并建立一些輔助運(yùn)維系統(tǒng):
- 首先服務(wù)之間遠(yuǎn)程調(diào)用,服務(wù)的注冊和發(fā)現(xiàn)機(jī)制必須有好的分布式服務(wù)框架(類似dubbo)支持,方便做服務(wù)之間的調(diào)用配置管理。
- 部署的服務(wù)進(jìn)程增加以后,對應(yīng)的日志文件也會增多,這時再使用ssh到服務(wù)器上查看日志,這個工作量也是可想而知的。我們對業(yè)務(wù)日志做了規(guī)范化約束,然后使用ELK技術(shù)棧搭建了日志集中化管理系統(tǒng)。
- 為了能夠?qū)€上的所有服務(wù)有個全局的把控,我們根據(jù)注冊中心的數(shù)據(jù)搭建了服務(wù)的管理系統(tǒng),展示各個維度的統(tǒng)計數(shù)據(jù),例如,每個主機(jī)上多少個應(yīng)用,每個應(yīng)用提供了多少個服務(wù),同時又消費(fèi)了多少個服務(wù)等等。還有一些針對服務(wù)的約束規(guī)則的告警信息,例如某個應(yīng)用消費(fèi)的服務(wù),卻沒有服務(wù)提供者等。還有一個整體的應(yīng)用之間的依賴關(guān)系圖。
- 為了優(yōu)化系統(tǒng)結(jié)構(gòu)和排查問題,搭建了服務(wù)的監(jiān)控系統(tǒng),這個是依賴分布式服務(wù)框架打印的預(yù)統(tǒng)計日志的,通過這些日志分析得到一個整體的服務(wù)監(jiān)控功能,例如每個服務(wù)的響應(yīng)時間、錯誤率等。還有調(diào)用鏈跟蹤功能,排查問題時使用它來跟蹤請求信息。
- 服務(wù)治理功能用來及時地對線上項目做一些調(diào)整,例如限流、降級、負(fù)載均衡、超時、路由、隔離容錯等。
有了以上這些系統(tǒng)輔助,我們的微服務(wù)化改造之路平坦了許多。
在進(jìn)行微服務(wù)化改造的時候,接口的設(shè)計上遇到了一個問題,服務(wù)之間的調(diào)用是通過私有協(xié)議進(jìn)行的RPC調(diào)用,這種調(diào)用中如何描述服務(wù)響應(yīng)成功之外的其他異常情況?
1.采用Exception類,定義各種異常類來描述異常情況。
2.采用錯誤碼+錯誤描述。
其中方案1采用的Exception類,是我們平時java中定義進(jìn)程內(nèi)部接口最常見的方法,這種方法的優(yōu)點(diǎn)是,能夠使調(diào)用接口的一方的代碼更簡潔,通過try...catch來處理,如果不需要處理的話,在方法上聲明直接向上拋出即可。缺點(diǎn)是跨語言開發(fā)解析難度大增,而且日志中異常堆棧很多。方案2采用錯誤碼的形式恰恰相反,缺點(diǎn)就是每次調(diào)用都要判讀是否調(diào)用成功,增加代碼中的if語句。
針對這個問題,希望各位實踐分布式服務(wù)或者微服務(wù)的大神給出你們的實踐方案,瘋狂討論起來。
04 結(jié)束語
上述三節(jié)中講述了我們在構(gòu)建微服務(wù)的經(jīng)歷和一些感想,給其他準(zhǔn)備實施微服務(wù)和正在實施微服務(wù)的團(tuán)隊以參考,寫這篇文章的另一個目的就是希望能起到拋磚引玉的作用,希望能和其他團(tuán)隊進(jìn)一步交流。