原文如下:http://martinfowler.com/articles/microservices.html
微服務(wù)
有關(guān)這個(gè)新的技術(shù)架構(gòu)術(shù)語的定義
“微服務(wù)架構(gòu)”這個(gè)術(shù)語最近幾年橫空出世,來描述這樣一種特定的軟件設(shè)計(jì)方法,即以若干組可獨(dú)立部署的服務(wù)的方式進(jìn)行軟件應(yīng)用系統(tǒng)的設(shè)計(jì)。盡管這種架構(gòu)風(fēng)格尚無精確的定義,但其在下述方面還是存在一定的共性,即圍繞業(yè)務(wù)功能的組織、自動化部署、端點(diǎn)智能、和在編程語言和數(shù)據(jù)方面進(jìn)行去中心化的控制。
2014年3月25日
作者:
James Lewis是ThoughtWorks首席咨詢師,而且是該公司的技術(shù)顧問委員會成員。James對于采用相互協(xié)作的小型服務(wù)來構(gòu)建應(yīng)用系統(tǒng)的興趣,源自于他的整合大規(guī)模企業(yè)系統(tǒng)的工作背景。他已經(jīng)使用微服務(wù)構(gòu)建了許多系統(tǒng),而且?guī)啄暌詠硪呀?jīng)成為正在成長的微服務(wù)社區(qū)的積極參與者。
Martin Fowler是一位作者和演講者,并且在軟件開發(fā)行業(yè)中,他通常是最能說的那一位。他長期以來一直困惑于這樣的問題,即如何才能將軟件系統(tǒng)進(jìn)行組件化。那些聲稱已將軟件進(jìn)行組件化的聲音他聽到了很多,但是很少有能讓他滿意的。他希望微服務(wù)不要辜負(fù)其倡導(dǎo)者們對它的最初的期望。
目錄
微服務(wù)架構(gòu)的九大特性
特性一:“組件化”與“多服務(wù)”
特性二:圍繞“業(yè)務(wù)功能”組織團(tuán)隊(duì)
特性三:“做產(chǎn)品”而不是“做項(xiàng)目”
特性四:“智能端點(diǎn)”與“傻瓜管道”
特性五:“去中心化”地治理技術(shù)
特性六:“去中心化”地管理數(shù)據(jù)
特性七:“基礎(chǔ)設(shè)施”自動化
特性八:“容錯(cuò)”設(shè)計(jì)
特性九:“演進(jìn)式”設(shè)計(jì)
未來的方向是“微服務(wù)”嗎?
擴(kuò)展閱讀
一個(gè)微服務(wù)應(yīng)該有多大?
微服務(wù)與SOA
多種編程語言,多種選擇可能
“實(shí)戰(zhàn)檢驗(yàn)”的標(biāo)準(zhǔn)與“強(qiáng)制執(zhí)行”的標(biāo)準(zhǔn)
讓“方向正確地做事”更容易
"斷路器"與“可隨時(shí)上線的代碼”
“同步調(diào)用”有害
“微服務(wù)”——這是在軟件架構(gòu)這條熙熙攘攘的大街上出現(xiàn)的又一個(gè)新詞兒。我們自然會對它投過輕蔑的一瞥,但是這個(gè)小小的術(shù)語卻描述了一種引人入勝的軟件系統(tǒng)的風(fēng)格。最近幾年,我們已經(jīng)看到許多項(xiàng)目使用了這種風(fēng)格,而且至今其結(jié)果都是良好的,以至于對于我們許多ThoughtWorks的同事來說,它正在成為構(gòu)建企業(yè)應(yīng)用系統(tǒng)的缺省的風(fēng)格。然而,很不幸的是,我們找不到有關(guān)它的概要信息,即什么是微服務(wù)風(fēng)格,以及如何設(shè)計(jì)微服務(wù)風(fēng)格的架構(gòu)。
簡而言之,微服務(wù)架構(gòu)風(fēng)格[1]這種開發(fā)方法,是以開發(fā)一組小型服務(wù)的方式來開發(fā)一個(gè)獨(dú)立的應(yīng)用系統(tǒng)的。其中每個(gè)小型服務(wù)都運(yùn)行在自己的進(jìn)程中,并經(jīng)常采用HTTP資源API這樣輕量的機(jī)制來相互通信。這些服務(wù)圍繞業(yè)務(wù)功能進(jìn)行構(gòu)建,并能通過全自動的部署機(jī)制來進(jìn)行獨(dú)立部署。這些微服務(wù)可以使用不同的語言來編寫,并且可以使用不同的數(shù)據(jù)存儲技術(shù)。對這些微服務(wù)我們僅做最低限度的集中管理。
在開始介紹微服務(wù)風(fēng)格之前,將其與單塊(monolithic)風(fēng)格進(jìn)行對比還是很有用的:一個(gè)單塊應(yīng)用系統(tǒng)是以一個(gè)單個(gè)單元的方式來構(gòu)建的。企業(yè)應(yīng)用系統(tǒng)經(jīng)常包含三個(gè)主要部分:客戶端用戶界面、數(shù)據(jù)庫和服務(wù)端應(yīng)用系統(tǒng)。客戶端用戶界面包括HTML頁面和運(yùn)行在用戶機(jī)器的瀏覽器中的JavaScript。數(shù)據(jù)庫中包括許多表,這些表被插入一個(gè)公共的且通常為關(guān)系型的數(shù)據(jù)庫管理系統(tǒng)中。這個(gè)服務(wù)端的應(yīng)用系統(tǒng)就是一個(gè)單塊應(yīng)用——一個(gè)單個(gè)可執(zhí)行的邏輯程序[2]。對于該系統(tǒng)的任何改變,都會涉及構(gòu)建和部署上述服務(wù)端應(yīng)用系統(tǒng)的一個(gè)新版本。
在我的“微服務(wù)資源指南”中(http://martinfowler.com/microservices/)能找到有關(guān)微服務(wù)最好的文章、視頻、圖書和播客媒體。
這樣的單塊服務(wù)器是構(gòu)建上述系統(tǒng)的一種自然的方式。處理用戶請求的所有邏輯都運(yùn)行在一個(gè)單個(gè)的進(jìn)程內(nèi),從而能使用編程語言的基本特性,來把應(yīng)用系統(tǒng)劃分為類、函數(shù)和命名空間。通過精心設(shè)計(jì),就能在開發(fā)人員的筆記本電腦上運(yùn)行和測試這樣的應(yīng)用系統(tǒng),并且使用一個(gè)部署流水線來確保變更被很好地進(jìn)行了測試,并被部署到生產(chǎn)環(huán)境中。通過負(fù)載均衡器運(yùn)行許多實(shí)例,來將這個(gè)單塊應(yīng)用進(jìn)行橫向擴(kuò)展。
單塊應(yīng)用系統(tǒng)可以被成功地實(shí)現(xiàn),但是漸漸地,特別是隨著越來越多的應(yīng)用系統(tǒng)正被部署到云端,人們對它們開始表現(xiàn)出不滿。軟件變更受到了很大的限制,應(yīng)用系統(tǒng)的一個(gè)很小的部分的一處變更,也需要將整個(gè)單塊應(yīng)用系統(tǒng)進(jìn)行重新構(gòu)建和部署。隨著時(shí)間的推移,單塊應(yīng)用開始變得經(jīng)常難以保持一個(gè)良好的模塊化結(jié)構(gòu),這使得它變得越來越難以將一個(gè)模塊的變更的影響控制在該模塊內(nèi)。當(dāng)對系統(tǒng)進(jìn)行擴(kuò)展時(shí),不得不擴(kuò)展整個(gè)應(yīng)用系統(tǒng),而不能僅擴(kuò)展該系統(tǒng)中需要更多資源的那些部分。
圖1: 單塊應(yīng)用和微服務(wù)
這些不滿導(dǎo)致了微服務(wù)架構(gòu)風(fēng)格的誕生:以構(gòu)建一組小型服務(wù)的方式來構(gòu)建應(yīng)用系統(tǒng)。除了這些服務(wù)能被獨(dú)立地部署和擴(kuò)展,每一個(gè)服務(wù)還能提供一個(gè)穩(wěn)固的模塊邊界,甚至能允許使用不同的編程語言來編寫不同的服務(wù)。這些服務(wù)也能被不同的團(tuán)隊(duì)來管理。
我們并不認(rèn)為微服務(wù)風(fēng)格是一個(gè)新穎或創(chuàng)新的概念,它的起源至少可以追溯到Unix的設(shè)計(jì)原則。但是我們覺得,考慮微服務(wù)架構(gòu)的人還不夠多,并且如果對其加以使用,許多軟件的開發(fā)工作能變得更好。
[1]2011年5月在威尼斯附近的一個(gè)軟件架構(gòu)工作坊中,大家開始討論“微服務(wù)”這個(gè)術(shù)語,因?yàn)檫@個(gè)詞可以描述參會者們在架構(gòu)領(lǐng)域進(jìn)行探索時(shí)所見到的一種通用的架構(gòu)風(fēng)格。2012年5月,這群參會者決定將“微服務(wù)”作為描述這種架構(gòu)風(fēng)格的最貼切的名字。在2012年3月波蘭的克拉科夫市舉辦的“33rd Degree”技術(shù)大會上,本文作者之一James在其“Microservices - Java, the Unix Way”演講中以案例的形式談到了這些微服務(wù)的觀點(diǎn)(http://2012.33degree.org/talk/show/67),與此同時(shí),F(xiàn)red?George也表達(dá)了同樣的觀點(diǎn)(http://www.slideshare.net/fredgeorge/micro-service-architecure)。Netflix公司的Adrian?Cockcroft將這種方法描述為“細(xì)粒度的SOA”,并且作為先行者和本文下面所提到的眾人已經(jīng)著手在Web領(lǐng)域進(jìn)行了實(shí)踐——Joe Walnes, Dan North, Evan Botcher 和 Graham Tackley。
[2]“單塊”(monolith)這個(gè)術(shù)語已經(jīng)被Unix社區(qū)使用一段時(shí)間了。它出現(xiàn)在The Art of Unix Programming一書中,來描述那些變得龐大的系統(tǒng)。
微服務(wù)架構(gòu)的九大特性
雖然不能說存在微服務(wù)架構(gòu)風(fēng)格的正式定義,但是可以嘗試描述我們所見到的能夠被貼上微服務(wù)標(biāo)簽的那些架構(gòu)的共性。下面所描述的所有這些共性,并不是所有的微服務(wù)架構(gòu)都完全具備,但是我們確實(shí)期望大多數(shù)微服務(wù)架構(gòu)都具備這些共性中的大多數(shù)特性。盡管我們兩位作者已經(jīng)成為這個(gè)相當(dāng)松散的社區(qū)的活躍成員,但我們的本意還是試圖描述我們兩人在自己和自己所了解的團(tuán)隊(duì)的工作中所看到的情況。特別要指出,我們不會制定大家需要遵循的微服務(wù)的定義。
特性一:“組件化”與“多服務(wù)”
自我們開始從事軟件業(yè)已來,發(fā)現(xiàn)大家都有一個(gè)把組件插在一起來構(gòu)建系統(tǒng)的愿望,就像在物理世界中所看到的那樣。在過去幾十年中,我們已經(jīng)看到,在公共軟件庫方面已經(jīng)取得了相當(dāng)大的進(jìn)展,這些軟件庫是大多數(shù)編程語言平臺的組成部分。
當(dāng)談到組件時(shí),就會碰到一個(gè)有關(guān)定義的難題,即什么是組件?我們的定義是,一個(gè)組件就是一個(gè)可以獨(dú)立更換和升級的軟件單元。
微服務(wù)架構(gòu)也會使用軟件庫,但其將自身軟件進(jìn)行組件化的主要方法是將軟件分解為諸多服務(wù)。我們將軟件庫(libraries)定義為這樣的組件,即它能被鏈接到一段程序,且能通過內(nèi)存中的函數(shù)來進(jìn)行調(diào)用。然而,服務(wù)(services)是進(jìn)程外的組件,它們通過諸如web service請求或遠(yuǎn)程過程調(diào)用這樣的機(jī)制來進(jìn)行通信(這不同于許多面向?qū)ο蟮某绦蛑械膕ervice object概念[3])。
以使用服務(wù)(而不是以軟件庫)的方式來實(shí)現(xiàn)組件化的一個(gè)主要原因是,服務(wù)可被獨(dú)立部署。如果一個(gè)應(yīng)用系統(tǒng)[4]由在單個(gè)進(jìn)程中的多個(gè)軟件庫所組成,那么對任一組件做一處修改,都不得不重新部署整個(gè)應(yīng)用系統(tǒng)。但是如果該應(yīng)用系統(tǒng)被分解為多個(gè)服務(wù),那么對于一個(gè)服務(wù)的多處修改,僅需要重新部署這一個(gè)服務(wù)。當(dāng)然這也不是絕對的,一些變更服務(wù)接口的修改會導(dǎo)致多個(gè)服務(wù)之間的協(xié)同修改。但是一個(gè)良好的微服務(wù)架構(gòu)的目的,是通過內(nèi)聚的服務(wù)邊界和服務(wù)協(xié)議方面的演進(jìn)機(jī)制,來將這樣的修改變得最小化。
以服務(wù)的方式來實(shí)現(xiàn)組件化的另一個(gè)結(jié)果,是能獲得更加顯式的(explicit)組件接口。大多數(shù)編程語言并沒有一個(gè)良好的機(jī)制來定義顯式的發(fā)布接口(Published Interface,http://martinfowler.com/bliki/PublishedInterface.html)。通常情況下,這樣的接口僅僅是文檔聲明和團(tuán)隊(duì)紀(jì)律,來避免客戶端破壞組件的封裝,從而導(dǎo)致組件間出現(xiàn)過度緊密的耦合。通過使用顯式的遠(yuǎn)程調(diào)用機(jī)制,服務(wù)能更容易地避免這種情況發(fā)生。
如此這般地使用服務(wù),也會有不足之處。比起進(jìn)程內(nèi)調(diào)用,遠(yuǎn)程調(diào)用更加昂貴。所以遠(yuǎn)程調(diào)用API接口必須是粗粒度的,而這往往更加難以使用。如果需要修改組件間的職責(zé)分配,那么當(dāng)跨越進(jìn)程邊界時(shí),這種組件行為的改動會更加難以實(shí)現(xiàn)。
近似地,我們可以把一個(gè)個(gè)服務(wù)映射為一個(gè)個(gè)運(yùn)行時(shí)的進(jìn)程,但這僅僅是一個(gè)近似。一個(gè)服務(wù)可能包括總是在一起被開發(fā)和部署的多個(gè)進(jìn)程,比如一個(gè)應(yīng)用系統(tǒng)的進(jìn)程和僅被該服務(wù)使用的數(shù)據(jù)庫。
[3]許多面向?qū)ο蟮脑O(shè)計(jì)者,包括我們自己,都使用領(lǐng)域驅(qū)動設(shè)計(jì)中service object這個(gè)術(shù)語,來描述那種執(zhí)行一段未被綁定到一個(gè)entity對象上的重要的邏輯過程的對象。這不同于本文所討論的"service"的概念。可悲的是,service這個(gè)術(shù)語同時(shí)具有這兩個(gè)含義,我們必須忍受這樣的多義詞。
[4]我們認(rèn)為一個(gè)應(yīng)用系統(tǒng)是一個(gè)社會性的構(gòu)建單元(http://martinfowler.com/bliki/ApplicationBoundary.html),來將一個(gè)代碼庫、功能組和資金體(body?of funding)結(jié)合起來。
特性二:圍繞“業(yè)務(wù)功能”組織團(tuán)隊(duì)
當(dāng)在尋求將一個(gè)大型應(yīng)用系統(tǒng)分解成幾部分時(shí),公司管理層往往會聚焦在技術(shù)層面上,這會導(dǎo)致組建用戶界面團(tuán)隊(duì)、服務(wù)器端團(tuán)隊(duì)和數(shù)據(jù)庫團(tuán)隊(duì)。當(dāng)團(tuán)隊(duì)沿著這些技術(shù)線分開后,即使要實(shí)現(xiàn)軟件一個(gè)簡單的變更,也會導(dǎo)致跨團(tuán)隊(duì)的項(xiàng)目時(shí)延和預(yù)算審批。在這種情況下,聰明的團(tuán)隊(duì)會進(jìn)行局部優(yōu)化,“兩害相權(quán)取其輕”,來直接把代碼邏輯塞到他們能訪問到的任意應(yīng)用系統(tǒng)中。換句話說,這種情況會導(dǎo)致代碼邏輯散布在系統(tǒng)各處。這就是康威定律[5]在起作用的活生生的例子。
任何設(shè)計(jì)(廣義上的)系統(tǒng)的組織,都會產(chǎn)生這樣一個(gè)設(shè)計(jì),即該設(shè)計(jì)的結(jié)構(gòu)與該組織的溝通結(jié)構(gòu)相一致。
——梅爾文?康威(Melvyn Conway), 1967年
圖2:康威定律在起作用
微服務(wù)使用不同的方法來分解系統(tǒng),即根據(jù)業(yè)務(wù)功能(business capability)來將系統(tǒng)分解為若干服務(wù)。這些服務(wù)針對該業(yè)務(wù)領(lǐng)域提供多層次廣泛的軟件實(shí)現(xiàn),包括用戶界面、持久性存儲以及任何對外的協(xié)作性操作。因此,團(tuán)隊(duì)是跨職能的,它擁有軟件開發(fā)所需的全方位的技能:用戶體驗(yàn)、數(shù)據(jù)庫和項(xiàng)目管理。
圖3:被團(tuán)隊(duì)邊界所強(qiáng)化的服務(wù)邊界
以上述方式來組織團(tuán)隊(duì)的公司是www.comparethemarket.com。跨職能團(tuán)隊(duì)負(fù)責(zé)構(gòu)建和運(yùn)維每個(gè)產(chǎn)品,而每個(gè)產(chǎn)品被拆分為多個(gè)獨(dú)立的服務(wù),彼此通過一個(gè)消息總線來通信。
一個(gè)微服務(wù)應(yīng)該有多大?
盡管對于這種架構(gòu)風(fēng)格,“微服務(wù)”已經(jīng)成為一個(gè)流行的名字,但是這個(gè)名字確實(shí)會不幸地導(dǎo)致大家對服務(wù)規(guī)模的關(guān)注,并且產(chǎn)生了有關(guān)什么是“微”的爭論。在與微服務(wù)從業(yè)者的交談中,我們看到了有關(guān)服務(wù)的一系列規(guī)模。所聽到的最大的一個(gè)服務(wù)的規(guī)模,是遵循了亞馬遜的“兩個(gè)比薩團(tuán)隊(duì)”(即一個(gè)團(tuán)隊(duì)可以被兩個(gè)比薩所喂飽)的理念,這意味著這個(gè)團(tuán)隊(duì)不會多于12人。對于規(guī)模較小的服務(wù),我們已經(jīng)看到一個(gè)6人的團(tuán)隊(duì)在支持6個(gè)服務(wù)。
這引出了一個(gè)問題,即“每12人做一個(gè)服務(wù)”和“每人做一個(gè)服務(wù)”這樣有關(guān)服務(wù)規(guī)模的差距,是否已經(jīng)大到不能將兩者都納入微服務(wù)之下?此時(shí),我們認(rèn)為最好還是把它們歸為一類,但是隨著進(jìn)一步探索這種架構(gòu)風(fēng)格,絕對有可能我們將來會改變主意。
大型單塊應(yīng)用系統(tǒng)也可以始終根據(jù)業(yè)務(wù)功能來進(jìn)行模塊化設(shè)計(jì),雖然這并不常見。當(dāng)然,我們會敦促構(gòu)建單塊應(yīng)用系統(tǒng)的大型團(tuán)隊(duì)根據(jù)業(yè)務(wù)線來將自己分解為若干小團(tuán)隊(duì)。在這方面,我們已經(jīng)看到的主要問題是,他們往往是一個(gè)團(tuán)隊(duì)包含了太多的業(yè)務(wù)功能。如果這個(gè)“單塊”跨越了許多模塊的邊界,那么這個(gè)團(tuán)隊(duì)的每一個(gè)成員都難以記憶所有這些模塊的業(yè)務(wù)功能。此外,我們看到這些模塊的邊界需要大量的團(tuán)隊(duì)紀(jì)律性來強(qiáng)制維持。而實(shí)現(xiàn)組件化的服務(wù)所必要的更加顯式的邊界,能更加容易地保持團(tuán)隊(duì)邊界的清晰性。
[5]原始論文參見梅爾文?康威的網(wǎng)站:http://www.melconway.com/Home/Committees_Paper.html
特性三:“做產(chǎn)品”而不是“做項(xiàng)目”
我們所看的的大部分應(yīng)用系統(tǒng)的開發(fā)工作都使用項(xiàng)目模型:目標(biāo)是交付某一塊軟件,之后就認(rèn)為完工了。一旦完工后,軟件就被移交給維護(hù)團(tuán)隊(duì),接著那個(gè)構(gòu)建該軟件的項(xiàng)目團(tuán)隊(duì)就會被解散。
微服務(wù)的支持者們傾向于避免使用上述模型,而寧愿采納“一個(gè)團(tuán)隊(duì)在一個(gè)產(chǎn)品的整個(gè)生命周期中都應(yīng)該保持對其擁有”這樣的理念。通常認(rèn)為這一點(diǎn)源自亞馬遜的“誰構(gòu)建,誰運(yùn)行”(https://queue.acm.org/detail.cfm?id=1142065)的理念,即一個(gè)開發(fā)團(tuán)隊(duì)對一個(gè)在生產(chǎn)環(huán)境下運(yùn)行的軟件負(fù)全責(zé)。這會使開發(fā)人員每天都會關(guān)注軟件是如何在生產(chǎn)環(huán)境下運(yùn)行的,并且增進(jìn)他們與用戶的聯(lián)系,因?yàn)樗麄儽仨毘袚?dān)某些支持工作。
這樣的“產(chǎn)品”理念,是與業(yè)務(wù)功能的聯(lián)動綁定在一起的。它不會將軟件看作是一個(gè)待完成的功能集合,而是認(rèn)為存在這樣一個(gè)持續(xù)的關(guān)系,即軟件如何能助其客戶來持續(xù)增進(jìn)業(yè)務(wù)功能。
當(dāng)然,單塊應(yīng)用系統(tǒng)的開發(fā)工作也可以遵循上述“產(chǎn)品”理念,但是更細(xì)粒度的服務(wù),能讓服務(wù)的開發(fā)者與其用戶之間的個(gè)人關(guān)系的創(chuàng)建變得更加容易。
特性四:“智能端點(diǎn)”與“傻瓜管道”
當(dāng)在不同的進(jìn)程之間構(gòu)建各種通信結(jié)構(gòu)時(shí),我們已經(jīng)看到許多產(chǎn)品和方法,來強(qiáng)調(diào)將大量的智能特性納入通信機(jī)制本身。這種狀況的一個(gè)典型例子,就是“企業(yè)服務(wù)總線”(Enterprise Service Bus, ESB)。ESB產(chǎn)品經(jīng)常包括高度智能的設(shè)施,來進(jìn)行消息的路由、編制(choreography)、轉(zhuǎn)換,并應(yīng)用業(yè)務(wù)規(guī)則。
微服務(wù)社區(qū)主張采用另一種做法:智能端點(diǎn)(smart endpoints)和傻瓜管道(dumb pipes)。使用微服務(wù)所構(gòu)建的各個(gè)應(yīng)用的目標(biāo),都是盡可能地實(shí)現(xiàn)“高內(nèi)聚和低耦合”——他們擁有自己的領(lǐng)域邏輯,并且更多地是像經(jīng)典Unix的“過濾器”(filter)那樣來工作——即接收一個(gè)請求,酌情對其應(yīng)用業(yè)務(wù)邏輯,并產(chǎn)生一個(gè)響應(yīng)。這些應(yīng)用通過使用一些簡單的REST風(fēng)格的協(xié)議來進(jìn)行編制,而不去使用諸如下面這些復(fù)雜的協(xié)議,即"WS-編制"(WS-Choreography)、BPEL或通過位于中心的工具來進(jìn)行編排(orchestration)。
微服務(wù)最常用的兩種協(xié)議是:帶有資源API的HTTP“請求-響應(yīng)”協(xié)議,和輕量級的消息發(fā)送協(xié)議[6]。對于前一種協(xié)議的最佳表述是:
成為Web,而不是躲著Web (Be of the web, not behind the web)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ——Ian Robinson
1
這些微服務(wù)團(tuán)隊(duì)在開發(fā)中,使用在構(gòu)建萬維網(wǎng)(world wide web)時(shí)所使用的原則和協(xié)議(并且在很大程度上,這些原則和協(xié)議也是在構(gòu)建Unix系統(tǒng)時(shí)所使用的)。那些被使用過的HTTP資源,通常能被開發(fā)或運(yùn)維人員輕易地緩存起來。
最常用的第二種協(xié)議,是通過一個(gè)輕量級的消息總線來進(jìn)行消息發(fā)送。此時(shí)所選擇的基礎(chǔ)設(shè)施,通常是“傻瓜”(dumb)型的(僅僅像消息路由器所做的事情那樣傻瓜)——像RabbitMQ或ZeroMQ那樣的簡單實(shí)現(xiàn),即除了提供可靠的異步機(jī)制(fabric)以外不做其他任何事情——智能功能存在于那些生產(chǎn)和消費(fèi)諸多消息的各個(gè)端點(diǎn)中,即存在于各個(gè)服務(wù)中。
在一個(gè)單塊系統(tǒng)中,各個(gè)組件在同一個(gè)進(jìn)程中運(yùn)行。它們相互之間的通信,要么通過方法調(diào)用,要么通過函數(shù)調(diào)用來進(jìn)行。將一個(gè)單塊系統(tǒng)改造為若干微服務(wù)的最大問題,在于對通信模式的改變。僅僅將內(nèi)存中的方法調(diào)用轉(zhuǎn)換為RPC調(diào)用這樣天真的做法,會導(dǎo)致微服務(wù)之間產(chǎn)生繁瑣的通信,使得系統(tǒng)表現(xiàn)變糟。取而代之的是,需要用更粗粒度的協(xié)議來替代細(xì)粒度的服務(wù)間通信。
[6] 在極度強(qiáng)調(diào)高效性(Scale)的情況下,一些組織經(jīng)常會使用一些二進(jìn)制的消息發(fā)送協(xié)議——例如protobuf。即使是這樣,這些系統(tǒng)仍然會呈現(xiàn)出“智能端點(diǎn)和傻瓜管道”的特點(diǎn)——來在易讀性(transparency)與高效性之間取得平衡。當(dāng)然,大多數(shù)Web屬性和絕大多數(shù)企業(yè)并不需要作出這樣的權(quán)衡——獲得易讀性就已經(jīng)是一個(gè)很大的勝利了。
特性五:“去中心化”地治理技術(shù)
使用中心化的方式來對開發(fā)進(jìn)行治理,其中一個(gè)后果,就是趨向于在單一技術(shù)平臺上制定標(biāo)準(zhǔn)。經(jīng)驗(yàn)表明,這種做法會帶來局限性——不是每一個(gè)問題都是釘子,不是每一個(gè)方案都是錘子。我們更喜歡根據(jù)工作的不同來選用合理的工具。盡管那些單塊應(yīng)用系統(tǒng)能在一定程度上利用不同的編程語言,但是這并不常見。
如果能將單塊應(yīng)用的那些組件拆分成多個(gè)服務(wù),那么在構(gòu)建每個(gè)服務(wù)時(shí),就可以有選擇不同技術(shù)棧的機(jī)會。想要使用Node.js來搞出一個(gè)簡單的報(bào)表頁面?盡管去搞。想用C++來做一個(gè)特別出彩兒的近乎實(shí)時(shí)的組件?沒有問題。想要換一種不同風(fēng)格的數(shù)據(jù)庫,來更好地適應(yīng)一個(gè)組件的讀取數(shù)據(jù)的行為?可以重建。
微服務(wù)和SOA
當(dāng)我們談起微服務(wù)時(shí),一個(gè)常見的問題就會出現(xiàn):是否微服務(wù)僅僅是十多年前所看到的“面向服務(wù)的架構(gòu)”(Service Oriented Architecture, SOA)?這樣問是有道理的,因?yàn)槲⒎?wù)風(fēng)格非常類似于一些支持SOA的人所贊成的觀點(diǎn)。然而,問題在于SOA這個(gè)詞兒意味著太多不同的東西(http://martinfowler.com/bliki/ServiceOrientedAmbiguity.html)。而且大多數(shù)時(shí)候,我們所遇到的某些被稱作"SOA"的事物,明顯不同于本文所描述的風(fēng)格。這通常由于它們專注于ESB,來集成各個(gè)單塊應(yīng)用。
特別地,我們已經(jīng)看到如此之多的面向服務(wù)的拙劣實(shí)現(xiàn)——從將系統(tǒng)復(fù)雜性隱藏于ESB中的趨勢[7],到花費(fèi)數(shù)百萬進(jìn)行多年卻沒有交付任何價(jià)值的失敗項(xiàng)目,到頑固抑制變化發(fā)生的中心化技術(shù)治理模型——以至于有時(shí)覺得其所造成的種種問題真的不堪回首。
當(dāng)然,在微服務(wù)社區(qū)投入使用的許多技術(shù),源自各個(gè)開發(fā)人員將各種服務(wù)集成到各個(gè)大型組織的經(jīng)驗(yàn)。“容錯(cuò)讀取”(Tolerant Reader,?http://martinfowler.com/bliki/TolerantReader.html)模式就是這樣一個(gè)例子。對于Web的廣泛使用,使得人們不再使用一些中心化的標(biāo)準(zhǔn),而使用一些簡單的協(xié)議。坦率地說,這些中心化的標(biāo)準(zhǔn),其復(fù)雜性已經(jīng)達(dá)到令人吃驚的程度(http://wiki.apache.org/ws/WebServiceSpecifications)。(任何時(shí)候,如果需要一個(gè)本體(ontology)來管理其他各個(gè)本體,那么麻煩就大了。)
這種常見的SOA的表現(xiàn),已使得一些微服務(wù)的倡導(dǎo)者完全拒絕將自己貼上SOA的標(biāo)簽。盡管其他人會將微服務(wù)看作是SOA的一種形式[8],也許微服務(wù)就是以正確的形式來實(shí)現(xiàn)面向服務(wù)的SOA。不管是哪種情況,SOA意味著如此之多的不同事物,這表明用一個(gè)更加干凈利落的術(shù)語來命名這種架構(gòu)風(fēng)格是很有價(jià)值的。
當(dāng)然,僅僅能做事情,并不意味著這些事情就應(yīng)該被做——不過用微服務(wù)的方法把系統(tǒng)進(jìn)行拆分后,就擁有了技術(shù)選型的機(jī)會。
相比選用業(yè)界一般常用的技術(shù),構(gòu)建微服務(wù)的那些團(tuán)隊(duì)更喜歡采用不同的方法。與其選用一組寫在紙上已經(jīng)定義好的標(biāo)準(zhǔn),他們更喜歡編寫一些有用的工具,來讓其他開發(fā)者能夠使用,以便解決那些和他們所面臨的問題相似的問題。這些工具通常源自他們的微服務(wù)實(shí)施過程,并且被分享到更大規(guī)模的組織中,這種分享有時(shí)會使用內(nèi)部開源的模式來進(jìn)行。現(xiàn)在,git和github已經(jīng)成為事實(shí)上的首選版本控制系統(tǒng)。在企業(yè)內(nèi)部,開源的做法正在變得越來越普遍。
Netflix公司是遵循上述理念的好例子。將實(shí)用且經(jīng)過實(shí)戰(zhàn)檢驗(yàn)的代碼以軟件庫的形式共享出來,能鼓勵(lì)其他開發(fā)人員以相似的方式來解決相似的問題,當(dāng)然也為在需要的時(shí)候選用不同的方案留了一扇門。共享軟件庫往往集中在解決這樣的常見問題,即數(shù)據(jù)存儲、進(jìn)程間的通信和下面要進(jìn)一步討論的基礎(chǔ)設(shè)施的自動化。
對于微服務(wù)社區(qū)來說,日常管理開銷這一點(diǎn)不是特別吸引人。這并不是說這個(gè)社區(qū)并不重視服務(wù)契約。恰恰相反,它們在社區(qū)里出現(xiàn)得更多。這正說明這個(gè)社區(qū)正在尋找對其進(jìn)行管理的各種方法。像“容錯(cuò)讀取”和“消費(fèi)者驅(qū)動的契約”(Consumer-Driven Contracts,?http://martinfowler.com/articles/consumerDrivenContracts.html)這樣的模式,經(jīng)常被運(yùn)用到微服務(wù)中。這些都有助于服務(wù)契約進(jìn)行獨(dú)立演進(jìn)。將執(zhí)行“消費(fèi)者驅(qū)動的契約”做為軟件構(gòu)建的一部分,能增強(qiáng)開發(fā)團(tuán)隊(duì)的信心,并提供所依賴的服務(wù)是否正常工作的快速反饋。實(shí)際上,我們了解到一個(gè)在澳洲的團(tuán)隊(duì)就是使用“消費(fèi)者驅(qū)動的契約”來驅(qū)動構(gòu)建多個(gè)新服務(wù)的。他們使用了一些簡單的工具,來針對每一個(gè)服務(wù)定義契約。甚至在新服務(wù)的代碼編寫之前,這件事就已經(jīng)成為自動化構(gòu)建的一部分了。接下來服務(wù)僅被構(gòu)建到剛好能滿足契約的程度——這是一個(gè)在構(gòu)建新軟件時(shí)避免YAGNI[9]困境的優(yōu)雅方法。這些技術(shù)和工具在契約周邊生長出來,由于減少了服務(wù)之間在時(shí)域(temporal)上的耦合,從而抑制了對中心契約管理的需求。
多種編程語言,多種選擇可能
做為一個(gè)平臺,JVM的發(fā)展僅僅是一個(gè)將各種編程語言混合到一個(gè)通用平臺的最新例證。近十年以來,在平臺外層實(shí)現(xiàn)更高層次的編程語言,來利用更高層次的抽象,已經(jīng)成為一個(gè)普遍做法。同樣,在平臺底層以更低層次的編程語言編寫性能敏感的代碼也很普遍。然而,許多單塊系統(tǒng)并不需要這種級別的性能優(yōu)化,另外DSL和更高層次的抽象也不常用(這令我們感到失望)。相反,許多單塊應(yīng)用通常就使用單一編程語言,并且有對所使用的技術(shù)數(shù)量進(jìn)行限制的趨勢[10]。
或許去中心化地治理技術(shù)的極盛時(shí)期,就是亞馬遜的“誰構(gòu)建,誰運(yùn)行”的風(fēng)氣開始普及的時(shí)候。各個(gè)團(tuán)隊(duì)負(fù)責(zé)其所構(gòu)建的軟件的所有方面的工作,其中包括7 x 24地對軟件進(jìn)行運(yùn)維。將運(yùn)維這一級別的職責(zé)下放到團(tuán)隊(duì)這種做法,目前絕對不是主流。但是我們確實(shí)看到越來越多的公司,將運(yùn)維的職責(zé)交給各個(gè)開發(fā)團(tuán)隊(duì)。Netflix就是已經(jīng)形成這種風(fēng)氣的另一個(gè)組織[11]。避免每天凌晨3點(diǎn)被枕邊的尋呼機(jī)叫醒,無疑是在程序員編寫代碼時(shí)令其專注質(zhì)量的強(qiáng)大動力。而這些想法,與那些傳統(tǒng)的中心化技術(shù)治理的模式具有天壤之別。
[7] 忍不住要提一下Jim Webber的說法:ESB表示Egregious Spaghetti Box(一盒極爛的意大利面條,http://www.infoq.com/presentations/soa-without-esb)。
[8] Netflix讓SOA與微服務(wù)之間的聯(lián)系更加明確——直到最近這家公司還將他們的架構(gòu)風(fēng)格稱為“細(xì)粒度的SOA”。
[9] “YAGNI” 或者 “You Aren’t Going To Need It”(你不會需要它)是極限編程的一條原則(http://c2.com/cgi/wiki?YouArentGonnaNeedIt)和勸誡,指的是“除非到了需要的時(shí)候,否則不要添加新功能”。
[10] 單塊系統(tǒng)使用單一編程語言,這樣講有點(diǎn)言不由衷——為了在今天的Web上構(gòu)建各種系統(tǒng),可能要了解JavaScript、XHTML、CSS、服務(wù)器端的編程語言、SQL和一種ORM的方言。很難說只有一種單一編程語言,但是我們的意思你是懂得的。
[11] Adrian Cockcroft在他2013年11月于Flowcon技術(shù)大會所做的一次精彩的演講(http://www.slideshare.net/adrianco/flowcon-added-to-for-cmg-keynote-talk-on-how-speed-wins-and-how-netflix-is-doing-continuous-delivery)中,特別提到了“開發(fā)人員自服務(wù)”和“開發(fā)人員運(yùn)行他們寫的東西”(原文如此)。
特性六:“去中心化”地管理數(shù)據(jù)
去中心化地管理數(shù)據(jù),其表現(xiàn)形式多種多樣。從最抽象的層面看,這意味著各個(gè)系統(tǒng)對客觀世界所構(gòu)建的概念模型,將彼此各不相同。當(dāng)在一個(gè)大型的企業(yè)中進(jìn)行系統(tǒng)集成時(shí),這是一個(gè)常見的問題。比如對于“客戶”這個(gè)概念,從銷售人員的視角看,就與從支持人員的視角看有所不同。從銷售人員的視角所看到的一些被稱之為“客戶”的事物,或許在支持人員的視角中根本找不到。而那些在兩個(gè)視角中都能看到的事物,或許各自具有不同的屬性。更糟糕的是,那些在兩個(gè)視角中具有相同屬性的事物,或許在語義上有微妙的不同。
上述問題在不同的應(yīng)用程序之間經(jīng)常出現(xiàn),同時(shí)也會出現(xiàn)在這些應(yīng)用程序內(nèi)部,特別是當(dāng)一個(gè)應(yīng)用程序被分成不同的組件時(shí)就會出現(xiàn)。思考這類問題的一個(gè)有用的方法,就是使用領(lǐng)域驅(qū)動設(shè)計(jì)(Domain-Driven Design, DDD)中的“限界上下文”(Bounded Context,http://martinfowler.com/bliki/BoundedContext.html)的概念。DDD將一個(gè)復(fù)雜的領(lǐng)域劃分為多個(gè)限界上下文,并且將其相互之間的關(guān)系用圖畫出來。這一劃分過程對于單塊和微服務(wù)架構(gòu)兩者都是有用的,而且就像前面有關(guān)“業(yè)務(wù)功能”一節(jié)中所討論的那樣,在服務(wù)和各個(gè)限界上下文之間所存在的自然的聯(lián)動關(guān)系,能有助于澄清和強(qiáng)化這種劃分。
“實(shí)戰(zhàn)檢驗(yàn)”的標(biāo)準(zhǔn)與“強(qiáng)制執(zhí)行”的標(biāo)準(zhǔn)
微服務(wù)的下述做法有點(diǎn)涇渭分明的味道,即他們趨向于避開被那些企業(yè)架構(gòu)組織所制定的硬性實(shí)施的標(biāo)準(zhǔn),而愉快地使用甚至傳播一些開放標(biāo)準(zhǔn),比如HTTP、ATOM和其他微格式的協(xié)議。
這里的關(guān)鍵區(qū)別是,這些標(biāo)準(zhǔn)是如何被制定以及如何被實(shí)施的。像諸如IETF這樣的組織所管理的各種標(biāo)準(zhǔn),只有達(dá)到下述條件才能稱為標(biāo)準(zhǔn),即該標(biāo)準(zhǔn)在全球更廣闊的地區(qū)有一些正在運(yùn)行的實(shí)現(xiàn)案例,而且這些標(biāo)準(zhǔn)經(jīng)常源自一些成功的開源項(xiàng)目。
這些標(biāo)準(zhǔn)組成了一個(gè)世界,它區(qū)別于來自下述另一個(gè)世界的許多標(biāo)準(zhǔn),即企業(yè)世界。企業(yè)世界中的標(biāo)準(zhǔn),經(jīng)常由這樣特點(diǎn)的組織來開發(fā),即缺乏用較新技術(shù)進(jìn)行編程的經(jīng)驗(yàn),或受到供應(yīng)商的過度影響。
如同在概念模型上進(jìn)行去中心化的決策一樣,微服務(wù)也在數(shù)據(jù)存儲上進(jìn)行去中心化的決策。盡管各個(gè)單塊應(yīng)用更愿意在邏輯上各自使用一個(gè)單獨(dú)的數(shù)據(jù)庫來持久化數(shù)據(jù),但是各家企業(yè)往往喜歡一系列單塊應(yīng)用共用一個(gè)單獨(dú)的數(shù)據(jù)庫——許多這樣的決策是被供應(yīng)商的各種版權(quán)商業(yè)模式所驅(qū)動出來的。微服務(wù)更喜歡讓每一個(gè)服務(wù)來管理其自有數(shù)據(jù)庫。其實(shí)現(xiàn)可以采用相同數(shù)據(jù)庫技術(shù)的不同數(shù)據(jù)庫實(shí)例,也可以采用完全不同的數(shù)據(jù)庫系統(tǒng)。這種方法被稱作“多語種持久化”(Polyglot Persistence,?http://martinfowler.com/bliki/PolyglotPersistence.html)。在一個(gè)單塊系統(tǒng)中也能使用多語種持久化,但是看起來這種方法在微服務(wù)中出現(xiàn)得更加頻繁。
在各個(gè)微服務(wù)之間將數(shù)據(jù)的職責(zé)進(jìn)行“去中心化”的管理,會影響軟件更新的管理。處理軟件更新的常用方法,是當(dāng)更新多個(gè)資源的時(shí)候,使用事務(wù)來保證一致性。這種方法經(jīng)常在單塊系統(tǒng)中被采用。
像這樣地使用事務(wù),有助于保持?jǐn)?shù)據(jù)一致性。但是在時(shí)域上會引發(fā)明顯的耦合,這樣當(dāng)在多個(gè)服務(wù)之間處理事務(wù)時(shí)會出現(xiàn)一致性問題。分布式事務(wù)實(shí)現(xiàn)起來難度之大是臭名遠(yuǎn)揚(yáng)的。為此,微服務(wù)架構(gòu)更強(qiáng)調(diào)在各個(gè)服務(wù)之間進(jìn)行“無事務(wù)”的協(xié)調(diào)(http://www.enterpriseintegrationpatterns.com/ramblings/18_starbucks.html)。這源自微服務(wù)社區(qū)明確地認(rèn)識到下述兩點(diǎn),即數(shù)據(jù)一致性可能只要求數(shù)據(jù)在最終達(dá)到一致,并且一致性問題能夠通過補(bǔ)償操作來進(jìn)行處理。
對于許多開發(fā)團(tuán)隊(duì)來說,選擇這種方式來管理數(shù)據(jù)的“非一致性”,是一個(gè)新的挑戰(zhàn)。但這也經(jīng)常符合在商業(yè)上的實(shí)踐做法。通常情況下,為了快速響應(yīng)需求,商家們都會處理一定程度上的數(shù)據(jù)“非一致性”,來通過做某種反向過程進(jìn)行錯(cuò)誤處理。只要修復(fù)錯(cuò)誤的成本,與在保持更大的數(shù)據(jù)一致性卻導(dǎo)致丟了生意所產(chǎn)生的成本相比,前者更低,那么這種“非一致性”地管理數(shù)據(jù)的權(quán)衡就是值得的。
特性七:“基礎(chǔ)設(shè)施”自動化
基礎(chǔ)設(shè)施自動化技術(shù)在過去幾年里已經(jīng)得到長足的發(fā)展。云的演進(jìn),特別是AWS的發(fā)展,已經(jīng)降低了構(gòu)建、部署和運(yùn)維微服務(wù)的操作復(fù)雜性。
許多使用微服務(wù)構(gòu)建的產(chǎn)品和系統(tǒng),正在被這樣的團(tuán)隊(duì)所構(gòu)建,即他們都具備極其豐富的“持續(xù)交付”(http://martinfowler.com/bliki/ContinuousDelivery.html)和其前身“持續(xù)集成”(http://martinfowler.com/articles/continuousIntegration.html)的經(jīng)驗(yàn)。用這種方法構(gòu)建軟件的各個(gè)團(tuán)隊(duì),廣泛采用了基礎(chǔ)設(shè)施的自動化技術(shù)。如下圖的構(gòu)建流水線所示:
圖5:基本的構(gòu)建流水線
因?yàn)楸疚牟⒉皇且黄嘘P(guān)持續(xù)交付的文章,所以下面僅提請大家注意兩個(gè)持續(xù)交付的關(guān)鍵特點(diǎn)。為了盡可能地獲得對正在運(yùn)行的軟件的信心,需要運(yùn)行大量的自動化測試。讓可工作的軟件達(dá)到“晉級”(Promotion)狀態(tài)從而“推上”流水線,就意味著可以在每一個(gè)新的環(huán)境中,對軟件進(jìn)行自動化部署。
一個(gè)單塊應(yīng)用程序,能夠相當(dāng)愉快地在上述各個(gè)環(huán)境中,被構(gòu)建、測試和推送。其結(jié)果是,一旦在下述工作上進(jìn)行了投入,即針對一個(gè)單塊系統(tǒng)將其通往生產(chǎn)環(huán)境的通道進(jìn)行自動化,那么部署更多的應(yīng)用系統(tǒng)似乎就不再可怕。記住,持續(xù)交付的目的之一,是讓“部署”工作變得“無聊”。所以不管是一個(gè)還是三個(gè)應(yīng)用系統(tǒng),只要部署工作依舊很“無聊”,那么就沒什么可擔(dān)心的了[12]。
讓“方向正確地做事”更容易
那些因?qū)崿F(xiàn)持續(xù)交付和持續(xù)集成所增加的自動化工作的副產(chǎn)品,是創(chuàng)建一些對開發(fā)和運(yùn)維人員有用的工具。現(xiàn)在,能完成下述工作的工具已經(jīng)相當(dāng)常見了,即創(chuàng)建工件(artefacts)、管理代碼庫、啟動一些簡單的服務(wù)、或增加標(biāo)準(zhǔn)的監(jiān)控和日志功能。Web上最好的例子可能是Netflix提供的一套開源工具集(http://netflix.github.io/),但也有其他一些好工具,包括我們已經(jīng)廣泛使用的Dropwizard (http://dropwizard.codahale.com/)。
我們所看到的各個(gè)團(tuán)隊(duì)在廣泛使用基礎(chǔ)設(shè)施自動化實(shí)踐的另一個(gè)領(lǐng)域,是在生產(chǎn)環(huán)境中管理各個(gè)微服務(wù)。與前面我們對比單塊系統(tǒng)和微服務(wù)所說的正相反,只要部署工作很無聊,那么在這一點(diǎn)上單塊系統(tǒng)和微服務(wù)就沒什么區(qū)別。然而,兩者在運(yùn)維領(lǐng)域的情況卻截然不同。
圖6:兩者的模塊部署經(jīng)常會有差異
[12] 這里我們又有點(diǎn)言不由衷了。 很明顯,在更復(fù)雜的網(wǎng)絡(luò)拓?fù)淅铮渴鸶嗟姆?wù),會比部署一個(gè)單獨(dú)的單塊系統(tǒng)要更加困難。幸運(yùn)的是,有一些模式能夠減少其中的復(fù)雜性——但對于工具的投資還是必須的。
特性八:“容錯(cuò)”設(shè)計(jì)
使用各個(gè)微服務(wù)來替代組件,其結(jié)果是各個(gè)應(yīng)用程序需要設(shè)計(jì)成能夠容忍這些服務(wù)所出現(xiàn)的故障。如果服務(wù)提供方不可用,那么任何對該服務(wù)的調(diào)用都會出現(xiàn)故障。客戶端要盡可能優(yōu)雅地應(yīng)對這種情況。與一個(gè)單塊設(shè)計(jì)相比,這是一個(gè)劣勢。因?yàn)檫@會引人額外的復(fù)雜性來處理這種情況。為此,各個(gè)微服務(wù)團(tuán)隊(duì)在不斷地反思:這些服務(wù)故障是如何影響用戶體驗(yàn)的。Netflix公司所研發(fā)的開源測試工具Simian Army(https://github.com/Netflix/SimianArmy),能夠誘導(dǎo)服務(wù)發(fā)生故障,甚至能誘導(dǎo)一個(gè)數(shù)據(jù)中心在工作日發(fā)生故障,來測試該應(yīng)用的彈性和監(jiān)控能力。
這種在生產(chǎn)環(huán)境中所進(jìn)行的自動化測試,能足以讓大多數(shù)運(yùn)維組織興奮得渾身顫栗,就像在一周的長假即將到來前那樣。這并不是說單塊架構(gòu)風(fēng)格不能構(gòu)建先進(jìn)的監(jiān)控系統(tǒng)——只是根據(jù)我們的經(jīng)驗(yàn),這在單塊系統(tǒng)中并不常見罷了。
"斷路器"與“可隨時(shí)上線的代碼”
“斷路器”(Circuit Breaker,?http://martinfowler.com/bliki/CircuitBreaker.html)一詞與其他一些模式一起出現(xiàn)在《發(fā)布!》(Release It!,?http://www.amazon.com/gp/product/B00A32NXZO?ie=UTF8&tag=martinfowlerc-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=B00A32NXZO)一書中,例如隔板(Bulkhead)和超時(shí)(Timeout)。當(dāng)構(gòu)建彼此通信的應(yīng)用系統(tǒng)時(shí),將這些模式加以綜合運(yùn)用就變得至關(guān)重要。Netflix公司的這篇很精彩的博客(http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html)解釋了這些模式是如何應(yīng)用的。
因?yàn)楦鱾€(gè)服務(wù)可以在任何時(shí)候發(fā)生故障,所以下面兩件事就變得很重要,即能夠快速地檢測出故障,而且在可能的情況下能夠自動恢復(fù)服務(wù)。各個(gè)微服務(wù)的應(yīng)用都將大量的精力放到了應(yīng)用程序的實(shí)時(shí)監(jiān)控上,來檢查“架構(gòu)元素指標(biāo)”(例如數(shù)據(jù)庫每秒收到多少請求)和“業(yè)務(wù)相關(guān)指標(biāo)”(例如系統(tǒng)每分鐘收到多少訂單)。當(dāng)系統(tǒng)某個(gè)地方出現(xiàn)問題,語義監(jiān)控系統(tǒng)能提供一個(gè)預(yù)警,來觸發(fā)開發(fā)團(tuán)隊(duì)進(jìn)行后續(xù)的跟進(jìn)和調(diào)查工作。
這對于一個(gè)微服務(wù)架構(gòu)是尤其重要的,因?yàn)槲⒎?wù)對于服務(wù)編制(choreography)和事件協(xié)作(http://martinfowler.com/eaaDev/EventCollaboration.html)的偏好,會導(dǎo)致“突發(fā)行為”。盡管許多權(quán)威人士對于偶發(fā)事件的價(jià)值持積極態(tài)度,但事實(shí)上,“突發(fā)行為”有時(shí)是一件壞事。在能夠快速發(fā)現(xiàn)有壞處的“突發(fā)行為”并進(jìn)行修復(fù)的方面,監(jiān)控是至關(guān)重要的。
單塊系統(tǒng)也能構(gòu)建得像微服務(wù)那樣來實(shí)現(xiàn)透明的監(jiān)控系統(tǒng)——實(shí)際上,它們也應(yīng)該如此。差別是,絕對需要知道那些運(yùn)行在不同進(jìn)程中的服務(wù),在何時(shí)斷掉了。而如果在同一個(gè)進(jìn)程內(nèi)使用軟件庫的話,這種透明的監(jiān)控系統(tǒng)就用處不大了。
“同步調(diào)用”有害
一旦在一些服務(wù)之間進(jìn)行多個(gè)同步調(diào)用,就會遇到宕機(jī)的乘法效應(yīng)。簡而言之,這意味著整個(gè)系統(tǒng)的宕機(jī)時(shí)間,是每一個(gè)單獨(dú)模塊各自宕機(jī)時(shí)間的乘積。此時(shí)面臨著一個(gè)選擇:是讓模塊之間的調(diào)用異步,還是去管理宕機(jī)時(shí)間?在英國衛(wèi)報(bào)網(wǎng)站www.guardian.co.uk,他們在新平臺上實(shí)現(xiàn)了一個(gè)簡單的規(guī)則——每一個(gè)用戶請求都對應(yīng)一個(gè)同步調(diào)用。然而在Netflix公司,他們重新設(shè)計(jì)的平臺API將異步性構(gòu)建到API的機(jī)制(fabric)中。
那些微服務(wù)團(tuán)隊(duì)希望在每一個(gè)單獨(dú)的服務(wù)中,都能看到先進(jìn)的監(jiān)控和日志記錄裝置。例如顯示“運(yùn)行/宕機(jī)”狀態(tài)的儀表盤,和各種運(yùn)維和業(yè)務(wù)相關(guān)的指標(biāo)。另外我們經(jīng)常在工作中會碰到這樣一些細(xì)節(jié),即斷路器的狀態(tài)、當(dāng)前的吞吐率和延遲,以及其他一些例子。
特性九:“演進(jìn)式”設(shè)計(jì)
那些微服務(wù)的從業(yè)者們,通常具有演進(jìn)式設(shè)計(jì)的背景,而且通常將服務(wù)的分解,視作一個(gè)額外的工具,來讓應(yīng)用開發(fā)人員能夠控制應(yīng)用系統(tǒng)中的變化,而無須減少變化的發(fā)生。變化控制并不一定意味著要減少變化——在正確的態(tài)度和工具的幫助下,就能在軟件中讓變化發(fā)生得頻繁、快速且經(jīng)過了良好的控制。
每當(dāng)試圖要將軟件系統(tǒng)分解為各個(gè)組件時(shí),就會面臨這樣的決策,即如何進(jìn)行切分——我們決定切分應(yīng)用系統(tǒng)時(shí)應(yīng)該遵循的原則是什么?一個(gè)組件的關(guān)鍵屬性,是具有獨(dú)立更換和升級的特點(diǎn)[13]——這意味著,需要尋找這些點(diǎn),即想象著能否在其中一個(gè)點(diǎn)上重寫該組件,而無須影響該組件的其他合作組件。事實(shí)上,許多做微服務(wù)的團(tuán)隊(duì)會更進(jìn)一步,他們明確地預(yù)期許多服務(wù)將來會報(bào)廢,而不是守著這些服務(wù)做長期演進(jìn)。
英國衛(wèi)報(bào)網(wǎng)站是一個(gè)好例子。原先該網(wǎng)站是一個(gè)以單塊系統(tǒng)的方式來設(shè)計(jì)和構(gòu)建的應(yīng)用系統(tǒng),然而它已經(jīng)開始向微服務(wù)方向進(jìn)行演進(jìn)了。原先的單塊系統(tǒng)依舊是該網(wǎng)站的核心,但是在添加新特性時(shí)他們愿意以構(gòu)建一些微服務(wù)的方式來進(jìn)行添加,而這些微服務(wù)會去調(diào)用原先那個(gè)單塊系統(tǒng)的API。當(dāng)在開發(fā)那些本身就帶有臨時(shí)性特點(diǎn)的新特性時(shí),這種方法就特別方便,例如開發(fā)那些報(bào)道一個(gè)體育賽事的專門頁面。當(dāng)使用一些快速的開發(fā)語言時(shí),像這樣的網(wǎng)站頁面就能被快速地整合起來。而一旦賽事結(jié)束,這樣頁面就可以被刪除。在一個(gè)金融機(jī)構(gòu)中,我們已經(jīng)看到了一些相似的做法,即針對一個(gè)市場機(jī)會,一些新的服務(wù)可以被添加進(jìn)來。然后在幾個(gè)月甚至幾周之后,這些新服務(wù)就作廢了。
這種強(qiáng)調(diào)可更換性的特點(diǎn),是模塊化設(shè)計(jì)一般性原則的一個(gè)特例,通過“變化模式”(pattern of change)[14]來驅(qū)動進(jìn)行模塊化的實(shí)現(xiàn)。大家都愿意將那些能在同時(shí)發(fā)生變化的東西,放到同一個(gè)模塊中。系統(tǒng)中那些很少發(fā)生變化的部分,應(yīng)該被放到不同的服務(wù)中,以區(qū)別于那些當(dāng)前正在經(jīng)歷大量變動(churn)的部分。如果發(fā)現(xiàn)需要同時(shí)反復(fù)變更兩個(gè)服務(wù)時(shí),這就是它們兩個(gè)需要被合并的一個(gè)信號。
把一個(gè)個(gè)組件放入一個(gè)個(gè)服務(wù)中,增大了作出更加精細(xì)的軟件發(fā)布計(jì)劃的機(jī)會。對于一個(gè)單塊系統(tǒng),任何變化都需要做一次整個(gè)應(yīng)用系統(tǒng)的全量構(gòu)建和部署。然而,對于一個(gè)個(gè)微服務(wù)來說,只需要重新部署修改過的那些服務(wù)就夠了。這能簡化并加快發(fā)布過程。但缺點(diǎn)是:必須要考慮當(dāng)一個(gè)服務(wù)發(fā)生變化時(shí),依賴它并對其進(jìn)行消費(fèi)的其他服務(wù)將無法工作。傳統(tǒng)的集成方法是試圖使用版本化來解決這個(gè)問題。但在微服務(wù)世界中,大家更喜歡將版本化作為最后萬不得已的手段(http://martinfowler.com/articles/enterpriseREST.html#versioning)來使用。我們可以通過下述方法來避免許多版本化的工作,即把各個(gè)服務(wù)設(shè)計(jì)得盡量能夠容錯(cuò),來應(yīng)對其所依賴的服務(wù)所發(fā)生的變化。
[13] 事實(shí)上,Dan North將這種架構(gòu)風(fēng)格稱作“可更換的組件架構(gòu)”,而不是微服務(wù)。因?yàn)檫@看起來似乎是在談微服務(wù)特性的一個(gè)子集,所以我們選擇將其歸類為微服務(wù)。
[14] Kent Beck在《實(shí)現(xiàn)模式》(Implementation Patterns,?http://www.amazon.com/gp/product/0321413091?ie=UTF8&tag=martinfowlerc-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0321413091)一書中,將其作為他的一條設(shè)計(jì)原則而強(qiáng)調(diào)出來。
未來的方向是“微服務(wù)”嗎?
我們寫這篇文章的主要目的,是來解釋有關(guān)微服務(wù)的主要思路和原則。在花了一點(diǎn)時(shí)間做了這件事后,我們清楚地認(rèn)識到,微服務(wù)架構(gòu)風(fēng)格是一個(gè)重要的理念——在研發(fā)企業(yè)應(yīng)用系統(tǒng)時(shí),值得對它進(jìn)行認(rèn)真考慮。我們最近已經(jīng)使用這種風(fēng)格構(gòu)建了一些系統(tǒng),并且了解到其他一些團(tuán)隊(duì)也在已經(jīng)使用并贊同這種方法。
我們所了解到的那些在某種程度上做為這種架構(gòu)風(fēng)格的實(shí)踐先驅(qū)包括:亞馬遜、Netflix、英國衛(wèi)報(bào)(http://www.theguardian.com/international)、英國政府?dāng)?shù)字化服務(wù)中心(https://gds.blog.gov.uk/)、realestate.com.au、Forward和comparethemarket.com。在2013年的技術(shù)大會圈子充滿了各種各樣的正在轉(zhuǎn)向可歸類為微服務(wù)的公司案例——包括Travis CI。另外還有大量的組織,它們長期以來一直在做著我們可以歸類為微服務(wù)的產(chǎn)品,卻從未使用過這個(gè)名字(這通常被標(biāo)記為SOA——盡管正如我們所說,SOA會表現(xiàn)出各種自相矛盾的形式[15])。
盡管有這些正面的經(jīng)驗(yàn),然而并不是說我們確信微服務(wù)是軟件架構(gòu)的未來的方向。盡管到目前為止,與單塊應(yīng)用系統(tǒng)相比,我們對于所經(jīng)歷過的微服務(wù)的評價(jià)是積極的,但是我們也意識到這樣的事實(shí),即能供我們做出完整判斷的時(shí)間還不夠長。
通常,架構(gòu)決策所產(chǎn)生的真正效果,只有在該決策做出若干年后才能真正顯現(xiàn)。我們已經(jīng)看到由帶著強(qiáng)烈的模塊化愿望的優(yōu)秀團(tuán)隊(duì)所做的一些項(xiàng)目,最終卻構(gòu)建出一個(gè)單塊架構(gòu),并在幾年之內(nèi)不斷腐化。許多人認(rèn)為,如果使用微服務(wù)就不大可能出現(xiàn)這種腐化,因?yàn)榉?wù)的邊界是明確的,而且難以隨意搞亂。然而,對于那些開發(fā)時(shí)間足夠長的各種系統(tǒng),除非我們已經(jīng)見識得足夠多,否則我們無法真正評價(jià)微服務(wù)架構(gòu)是如何成熟的。
我們的同事Sam Newman花了2014年的大部分時(shí)間撰寫了一本書(http://www.amazon.com/gp/product/1491950358?ie=UTF8&tag=martinfowlerc-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1491950358),來記述我們構(gòu)建微服務(wù)的經(jīng)驗(yàn)。如果想對這個(gè)話題深入下去,下一步就應(yīng)該是閱讀這本書。
有人覺得微服務(wù)或許很難成熟起來,這當(dāng)然是有原因的。在組件化上所做的任何工作的成功與否,取決于軟件與組件的匹配程度。準(zhǔn)確地搞清楚某個(gè)組件的邊界的位置應(yīng)該出現(xiàn)在哪里,是一件困難的工作。演進(jìn)式設(shè)計(jì)承認(rèn)難以對邊界進(jìn)行正確定位,所以它將工作的重點(diǎn)放到了易于對邊界進(jìn)行重構(gòu)之上。但是當(dāng)各個(gè)組件成為各個(gè)進(jìn)行遠(yuǎn)程通信的服務(wù)后,比起在單一進(jìn)程內(nèi)進(jìn)行各個(gè)軟件庫之間的調(diào)用,此時(shí)的重構(gòu)就變得更加困難。跨越服務(wù)邊界的代碼移動就變得困難起來。接口的任何變化,都需要在其各個(gè)參與者之間進(jìn)行協(xié)調(diào)。向后兼容的層次也需要被添加進(jìn)來。測試也會變得更加復(fù)雜。
另一個(gè)問題是,如果這些組件不能干凈利落地組合成一個(gè)系統(tǒng),那么所做的一切工作,僅僅是將組件內(nèi)的復(fù)雜性轉(zhuǎn)移到組件之間的連接之上。這樣做的后果,不僅僅是將復(fù)雜性搬了家,它還將復(fù)雜性轉(zhuǎn)移到那些不再明確且難以控制的邊界之上。當(dāng)在觀察一個(gè)小型且簡單的組件內(nèi)部時(shí),人們很容易覺得事情已經(jīng)變得更好了,然而他們卻忽視了服務(wù)之間雜亂的連接。
最后,還有一個(gè)團(tuán)隊(duì)技能的因素。新技術(shù)往往會被技術(shù)更加過硬的團(tuán)隊(duì)所采用。對于技術(shù)更加過硬的團(tuán)隊(duì)而更有效的一項(xiàng)技術(shù),不一定適用于一個(gè)技術(shù)略遜一籌的團(tuán)隊(duì)。我們已經(jīng)看到大量這樣的案例,那些技術(shù)略遜一籌的團(tuán)隊(duì)構(gòu)建出了雜亂的單塊架構(gòu)。當(dāng)這種雜亂發(fā)生到微服務(wù)身上時(shí),會出現(xiàn)什么情況?這需要花時(shí)間來觀察。一個(gè)糟糕的團(tuán)隊(duì),總會構(gòu)建一個(gè)糟糕的系統(tǒng)——在這種情況下,很難講微服務(wù)究竟是減少了雜亂,還是讓事情變得更糟。
我們聽到一個(gè)合理的說法,是說不要一上來就以微服務(wù)架構(gòu)做為起點(diǎn)。相反,要用一個(gè)單塊系統(tǒng)做為起點(diǎn)(http://martinfowler.com/bliki/MonolithFirst.html),并保持其模塊化。當(dāng)這個(gè)單塊系統(tǒng)出現(xiàn)了問題后,再將其分解為微服務(wù)。(盡管這個(gè)建議并不理想(http://martinfowler.com/articles/dont-start-monolith.html),因?yàn)橐粋€(gè)良好的單一進(jìn)程內(nèi)的接口,通常不是一個(gè)良好的服務(wù)接口。)
因此,我們持謹(jǐn)慎樂觀的態(tài)度來撰寫此文。到目前為止,我們已經(jīng)看到足夠多的有關(guān)微服務(wù)風(fēng)格的事物,并且覺得這是一條有價(jià)值去跋涉的道路(http://martinfowler.com/microservices/)。我們不能肯定地說,道路的盡頭在哪里。但是,軟件開發(fā)的挑戰(zhàn)之一,就是只能基于“目前手上擁有但還不夠完善”的信息來做出決策。
[15] SOA很難講是這段歷史的根源。當(dāng)SOA這個(gè)詞兒在本世紀(jì)初剛剛出現(xiàn)時(shí),我記得有人說:“我們很多年以來一直是這樣做的。”有一派觀點(diǎn)說,SOA這種風(fēng)格,將企業(yè)級計(jì)算早期COBOL程序通過數(shù)據(jù)文件來進(jìn)行通信的方式,視作自己的“根”。在另一個(gè)方向上,有人說“Erlang編程模型”與微服務(wù)是同一回事,只不過它被應(yīng)用到一個(gè)企業(yè)應(yīng)用的上下文中去了。