微服務(wù)
新的架構(gòu)術(shù)語——微服務(wù)
在過去的幾年興起了一個(gè)新的架構(gòu)術(shù)語——微服務(wù)。它用來描述將軟件應(yīng)用程序設(shè)計(jì)為一組可以獨(dú)立部署的服務(wù)的特定方法。這種架構(gòu)風(fēng)格雖然沒有明確的定義,但是其從組織,業(yè)務(wù)功能方面呈現(xiàn)出一些共同特征:自動(dòng)化部署,端點(diǎn)智能化,語言和數(shù)據(jù)去中心化管理。
微服務(wù)——在紛繁復(fù)雜的軟件架構(gòu)領(lǐng)域出現(xiàn)的又一新名詞。雖然我們的本能反應(yīng)是對此類事物不屑一顧。但是,我們發(fā)現(xiàn),微服務(wù)描述的軟件系統(tǒng)風(fēng)格越來越具有吸引力。我們看到在過去的幾年里許多項(xiàng)目采用了這種風(fēng)格,并且到目前為止效果是積極的。甚至,對于我許多同事來說,這已經(jīng)成為構(gòu)建企業(yè)級(jí)應(yīng)用的首選風(fēng)格。然而遺憾的是,沒有太多的關(guān)于概述什么是微服務(wù)風(fēng)格以及如何使用微服務(wù)的信息。
簡單來說,微服務(wù)架構(gòu)風(fēng)格是一種把單個(gè)應(yīng)用系統(tǒng)劃分成一套小的服務(wù)來進(jìn)行開發(fā),每個(gè)服務(wù)擁有自己的進(jìn)程, 彼此使用輕量級(jí)的機(jī)制,如HTTP資源API,進(jìn)行通信,這些服務(wù)圍繞業(yè)務(wù)功能構(gòu)建,通過全自動(dòng)化部署設(shè)施進(jìn)行獨(dú)立部署,對它們盡可能的分散管理,可以為它們選擇不同的開發(fā)語言,選擇不同的數(shù)據(jù)存儲(chǔ)技術(shù)存儲(chǔ)數(shù)據(jù)。
和單體風(fēng)格——使用單個(gè)單元構(gòu)建單體應(yīng)用的方法——進(jìn)行比較有助于對微服務(wù)風(fēng)格的說明。企業(yè)級(jí)應(yīng)用通常由三個(gè)部分構(gòu)成:客戶端UI(由運(yùn)行在客戶端瀏覽器里的HTML頁面和腳本組成)、數(shù)據(jù)庫(有許多插入到通用的,通常是關(guān)系型的DBMS的表組成)、服務(wù)端應(yīng)用。服務(wù)端應(yīng)用處理HTTP請求,執(zhí)行域邏輯,返回或更新數(shù)據(jù)庫數(shù)據(jù)以及選擇并構(gòu)建HTML視圖發(fā)送給瀏覽器。服務(wù)端應(yīng)用是一個(gè)單體——一個(gè)單一的邏輯執(zhí)行體。任何對系統(tǒng)的改變都需要為服務(wù)端應(yīng)用編譯并部署一個(gè)新的版本。
這樣的單體服務(wù)器是構(gòu)建系統(tǒng)的自然而然的方法。你處理請求的所有邏輯都運(yùn)行在單個(gè)進(jìn)程中。同時(shí)它允許你依靠編程語言的基本特性把應(yīng)用程序拆分成類、函數(shù)和命名空間等。需要注意的是,你可以在開發(fā)機(jī)器上運(yùn)行并測試應(yīng)用,使用部署管道確保對應(yīng)用的修改經(jīng)過適當(dāng)?shù)臏y試然后部署到生產(chǎn)環(huán)境。你可以在一個(gè)負(fù)載均衡后面運(yùn)行多個(gè)這樣的單體應(yīng)用實(shí)例來實(shí)現(xiàn)水平擴(kuò)展。
單體應(yīng)用可以成功,但人們也越來越對其感到沮喪,尤其是當(dāng)更多的應(yīng)用部署到云上的時(shí)候。(應(yīng)用各部分的的)變更周期被捆綁在一起——對應(yīng)用一個(gè)小的部分的修改都要求整個(gè)應(yīng)用重新構(gòu)建和部署。隨著時(shí)間的推移,單體應(yīng)用通常也很難維持一個(gè)好的模塊化結(jié)構(gòu),很難做到一個(gè)本來僅影響一個(gè)模塊的變更只需要修改這個(gè)模塊就行(注:也就是說:本來一個(gè)變更業(yè)務(wù)上看上去只需要修改某個(gè)模塊,但事實(shí)上,由于隨著時(shí)間的推移,系統(tǒng)漸漸"腐敗",模塊間耦合增加,導(dǎo)致需要修改其他的模塊。)。擴(kuò)展也只能就整個(gè)應(yīng)用而不是應(yīng)用的的某個(gè)部分進(jìn)行,從而導(dǎo)致消耗更多資源。

上述單體應(yīng)用帶來的困擾促使微服務(wù)架構(gòu)風(fēng)格的出現(xiàn)——用一組服務(wù)來構(gòu)建應(yīng)用系統(tǒng)。每個(gè)服務(wù)可以獨(dú)立部署和伸縮,每個(gè)服務(wù)都提供固定的模塊邊界,不同的服務(wù)甚至允許使用不同的編程語言來編寫。而且不同的服務(wù)可以由不同的團(tuán)隊(duì)來管理。
我們不會(huì)說微服務(wù)風(fēng)格是一種新穎的風(fēng)格或創(chuàng)新,因?yàn)樗母粗辽倏梢宰匪莸経nix設(shè)計(jì)之道。然而,我們認(rèn)為沒有足夠多的人在考慮使用微服務(wù)架構(gòu)。我們認(rèn)為如果使用微服務(wù)架構(gòu),軟件開發(fā)會(huì)變得更好。
微服務(wù)架構(gòu)特征
我們不說存在一個(gè)對微服務(wù)架構(gòu)風(fēng)格的正式定義。然而我們可以嘗試描述下我們所看到的符合這種架構(gòu)風(fēng)格的共同特征。與任何概括共同特征的定義一樣,不是所有的微服務(wù)架構(gòu)都具有所有的特征。但是我們希望大多數(shù)微服務(wù)架構(gòu)都具有大多數(shù)的特征。雖然我們這些作者已經(jīng)是這個(gè)相當(dāng)寬松的社區(qū)的積極分子,但我們的目的還是想描述在我們自己的工作中和我們所了解的團(tuán)隊(duì)做出的類似的努力中所看到的東西。不過,我們不會(huì)做出什么是符合微服務(wù)架構(gòu)的定義。
組件服務(wù)化
在軟件行業(yè),我們一直希望通過把組件組裝在一起的方式來構(gòu)建系統(tǒng)。就像我們看到的物理世界許多創(chuàng)造事物的方式一樣。在過去幾十年里,我們看到了作為大多數(shù)語言平臺(tái)一部分的大量的公共庫的顯著發(fā)展。
當(dāng)我們談?wù)摻M件的時(shí)候會(huì)碰到一個(gè)難于定義的問題,就是組件是什么?我們的定義是,組件是一個(gè)可獨(dú)立替換和升級(jí)的軟件單元。
微服務(wù)架構(gòu)會(huì)使用庫,但它實(shí)現(xiàn)組件化的主要方式是把系統(tǒng)拆分成服務(wù)。我們定義的庫是可以鏈接到程序并在內(nèi)存中被函數(shù)調(diào)用的組件,而服務(wù)則是通過Web服務(wù)請求或者RPC之類的機(jī)制進(jìn)行通信的進(jìn)程外的組件。(這和許多使用OO方法設(shè)計(jì)的程序里的服務(wù)對象是不同的概念)。
使用服務(wù)而不是庫作為組件的一個(gè)主要原因是服務(wù)是可獨(dú)立部署的。如果你有一個(gè)由位于單個(gè)進(jìn)程里的多個(gè)庫組成的應(yīng)用程序,那么對于任何一個(gè)庫的修改都會(huì)導(dǎo)致整個(gè)應(yīng)用程序的重新部署。然而,如果應(yīng)用程序被分解成多個(gè)服務(wù),那么你就可以做到許多針對單個(gè)服務(wù)的修改僅需要重新部署該服務(wù)本身。這不是絕對的,有些變更會(huì)改變服務(wù)接口,這會(huì)導(dǎo)致一些協(xié)調(diào)的發(fā)生(注:受影響的服務(wù)需要調(diào)整接口的調(diào)用)。但一個(gè)好的微服務(wù)架構(gòu)的目標(biāo)就是通過內(nèi)聚的服務(wù)邊界和服務(wù)契約機(jī)制的演進(jìn)讓影響最小化。
使用服務(wù)化組件另外一個(gè)結(jié)果是組件接口變得更清晰。許多語言沒有一個(gè)良好的機(jī)制來定義一個(gè)清晰的可發(fā)布接口。 通常我們僅通過文檔和規(guī)則來防止客戶破壞組件的封裝,這導(dǎo)致組件間過度緊密耦合。服務(wù)間通過明確的RPC機(jī)制進(jìn)行可以更容易避免這種情況發(fā)生。
但這也有缺點(diǎn)。RPC比進(jìn)程內(nèi)調(diào)用昂貴,而且這種RPC只能是粗粒度的,這往往導(dǎo)致使用起來比較笨拙。如果你需要改變組件間的職責(zé)分配,那么跨進(jìn)程邊界的行為遷移將更困難。
咋一看,我們發(fā)現(xiàn)服務(wù)就是對應(yīng)運(yùn)行時(shí)進(jìn)程,但這僅僅是表象。一個(gè)服務(wù)可以由多個(gè)總是一起開發(fā)和部署的進(jìn)程組成。比如一個(gè)服務(wù)有一個(gè)應(yīng)用進(jìn)程和僅被該服務(wù)使用的數(shù)據(jù)庫。
按業(yè)務(wù)功能劃分組織
當(dāng)要拆分一個(gè)大的應(yīng)用的時(shí)候,通常管理層主要從技術(shù)層面著手,從而會(huì)產(chǎn)生UI團(tuán)隊(duì)、服務(wù)端邏輯團(tuán)隊(duì)和數(shù)據(jù)庫團(tuán)隊(duì)。當(dāng)團(tuán)隊(duì)是這樣劃分的時(shí)候,即使一個(gè)簡單的變更也會(huì)導(dǎo)致跨團(tuán)隊(duì)進(jìn)行時(shí)間估計(jì)和預(yù)算審批。"機(jī)靈"的團(tuán)隊(duì)為了他們自己的便利會(huì)兩害相權(quán)取其輕——強(qiáng)制把邏輯放在他們能訪問的應(yīng)用之中。換句話說,邏輯將無處不在(注:大家為了避免跨團(tuán)隊(duì)溝通的麻煩,盡可能的在自己可控的范圍內(nèi)注入業(yè)務(wù)邏輯,導(dǎo)致“邏輯無處不在”,同時(shí)因?yàn)椴粫?huì)優(yōu)先考慮系統(tǒng)模塊高內(nèi)聚和低耦合,導(dǎo)致系統(tǒng)“腐化“)。下面是一個(gè)康威定律的例子。
任何設(shè)計(jì)系統(tǒng)(廣義上定義的)的組織設(shè)計(jì)出來的系統(tǒng),其結(jié)構(gòu)都將和組織溝通結(jié)構(gòu)一致。
——Melvyn Conway, 1967

微服務(wù)拆分的方法是不同的,拆分的服務(wù)是按照業(yè)務(wù)功能(業(yè)務(wù)面)來劃分的。這樣的服務(wù)需要為其業(yè)務(wù)功能完成全棧的軟件實(shí)現(xiàn),包括UI,持久存儲(chǔ)及所有的外部協(xié)作。因此,(實(shí)現(xiàn)服務(wù)的)團(tuán)隊(duì)也需要是跨功能的團(tuán)隊(duì),需要擁有開發(fā)所需的,如用戶體驗(yàn),數(shù)據(jù)庫和項(xiàng)目管理等完整的技能。

www.comparethemarket.com是這種組織的一家公司??绻δ軋F(tuán)隊(duì)負(fù)責(zé)構(gòu)建和營運(yùn)每個(gè)產(chǎn)品,每個(gè)產(chǎn)品被劃分成多個(gè)獨(dú)立的服務(wù),服務(wù)間使用消息總線通信。
大型單體應(yīng)用也總是能夠按業(yè)務(wù)功能進(jìn)行模塊化,雖然這種情況不常見。當(dāng)然我們會(huì)力促一個(gè)構(gòu)建單體應(yīng)用的大型團(tuán)隊(duì)按業(yè)務(wù)線拆分自己。我們在這里看到的主要問題是:他們往往是圍繞太多的上下文來組織的。假如單體應(yīng)用橫跨許多這樣的模塊化的邊界,對團(tuán)隊(duì)的個(gè)體來說是很難短期記住這些模塊的。并且,我們看到那些模塊化的邊線要求遵守大量的規(guī)則。組件服務(wù)化所必然要求更明確的分隔,這使得保持清晰的團(tuán)隊(duì)邊界更容易。
產(chǎn)品而非項(xiàng)目
我們看到,大多數(shù)應(yīng)用開發(fā)工作使用項(xiàng)目模式,其目標(biāo)是發(fā)布一些被認(rèn)為完成的軟件。軟件完工后被移交給運(yùn)維部門,然后項(xiàng)目團(tuán)隊(duì)解散。
微服務(wù)支持者傾向于避免這種模式,而是秉承一種團(tuán)隊(duì)?wèi)?yīng)該擁有整個(gè)產(chǎn)品的生命周期的理念。對這種理念的共同啟發(fā)是來自亞馬遜的“你構(gòu)建,你運(yùn)行”的理念。開發(fā)團(tuán)隊(duì)負(fù)責(zé)生產(chǎn)環(huán)境的軟件,這使得開發(fā)人員能夠和生產(chǎn)環(huán)境里的軟件保持日常的聯(lián)系,同時(shí)也增進(jìn)開發(fā)人員和客戶之間的聯(lián)系,因?yàn)樗麄冎辽僖袚?dān)一些客戶支持的工作。
產(chǎn)品心態(tài)與業(yè)務(wù)功能緊密相連。它與把軟件看作一套(需要開發(fā))完成的功能不同,它是一種持續(xù)的關(guān)系——軟件如何幫助用戶提高業(yè)務(wù)能力。
沒有理由解釋相同的做法(注:產(chǎn)品心態(tài))為什么不能應(yīng)用到單體應(yīng)用 。不過更小粒度的服務(wù)更易于建立服務(wù)開發(fā)者和用戶之間的人際關(guān)系。
智能端點(diǎn)和啞管道
當(dāng)在構(gòu)建不同進(jìn)程間的通信結(jié)構(gòu)的時(shí)候,我們看到許多產(chǎn)品和方法強(qiáng)調(diào)把重要的處理放到通信機(jī)制本身里面。一個(gè)很好的例子就是企業(yè)服務(wù)總線(ESB)。ESB產(chǎn)品通常會(huì)包含復(fù)雜的設(shè)施用于消息路由,編排和轉(zhuǎn)換,以及業(yè)務(wù)規(guī)則應(yīng)用。
但微服務(wù)社區(qū)更偏好另外一個(gè)方法:智能端點(diǎn)和啞管道。微服務(wù)構(gòu)建應(yīng)用程序以高內(nèi)聚低耦合為目標(biāo)。他們擁有自己的域邏輯,扮演著更像經(jīng)典Unix風(fēng)格的過濾器(filter)角色,接受請求,然后執(zhí)行相應(yīng)的邏輯,最后產(chǎn)生應(yīng)答。這是通過簡單的RESTful協(xié)議進(jìn)行編排,而不是如WS-Choreography 和BPEL等復(fù)雜協(xié)議或者中心化工具編排。
使用最廣泛的兩個(gè)協(xié)議是HTTP請求/應(yīng)答資源API和輕量消息。對前者最恰當(dāng)?shù)拿枋鍪牵?/p>
是web,而不是位于web之后
——Ian Robinson
微服務(wù)團(tuán)隊(duì)使用萬維網(wǎng)(更大程度上是Unix)相關(guān)的協(xié)議和原則。對開發(fā)和運(yùn)維來說,經(jīng)常使用的資源可以毫不費(fèi)力的緩存起來。
第二個(gè)普遍方法是使用輕量的消息總線發(fā)布消息。所選擇的基礎(chǔ)架構(gòu)典型的是啞的(僅僅是在扮演消息路由)——簡單的實(shí)現(xiàn),如RabbitMQ或者ZeroMQ,它們僅提供可靠異步管道。其他的處理仍然位于產(chǎn)生或消費(fèi)消息的服務(wù)端點(diǎn)中。
在單體應(yīng)用中,組件在進(jìn)程內(nèi)執(zhí)行,通過方法或函數(shù)調(diào)用進(jìn)行通信。把單體應(yīng)用改變成微服務(wù)最大的問題在于改變通信方式。直接把內(nèi)存中的方法調(diào)用轉(zhuǎn)換成RPC會(huì)導(dǎo)致通信的繁瑣且無法順利執(zhí)行。因此,你需要把這種細(xì)粒度的通信(函數(shù)調(diào)用)替換成粗粒度通信。
(注:智能終端,啞管道是相對類似基于ESB構(gòu)建起來的SOA中的啞終端,智能管道而言的。隨著微服務(wù)架構(gòu)的出現(xiàn),基于K8s+Service Mesh的服務(wù)網(wǎng)格系統(tǒng),已經(jīng)演變成了智能平臺(tái)和聚焦業(yè)務(wù)邏輯的智能服務(wù)。如下圖所示:

)
去中心化治理
中心化管治的結(jié)果最終都傾向于形成單一技術(shù)平臺(tái)的標(biāo)準(zhǔn)化。經(jīng)驗(yàn)表明這會(huì)造成一些局限性——不是每個(gè)問題都是釘子,也不是每個(gè)方案都是錘子(注:也就是說單一的方案不能解決所有的問題)。我們更喜歡使用合適的工具來完成相應(yīng)的工作。雖然單體應(yīng)用也可以一定程度上獲得使用不同開發(fā)語言的好處,但這并不普遍。
把單體應(yīng)用拆分成服務(wù)之后,我們就可以選擇不同的開發(fā)語言來構(gòu)建這些服務(wù)。你想使用Node.js起來一個(gè)簡單的報(bào)表頁面嗎?去弄吧。你想使用C++寫一個(gè)特別簡單的且近乎實(shí)時(shí)的組件嗎?好,沒問題。你想變換不同風(fēng)格的數(shù)據(jù)庫來更好地適應(yīng)組件的讀取行為嗎?我們有那種技術(shù)來重建它。
當(dāng)然,只是因?yàn)槟隳軌蜻@樣做,但并不意味著你應(yīng)該這樣做——你有這樣的選項(xiàng)來分割你的系統(tǒng)。
構(gòu)建微服務(wù)的團(tuán)隊(duì)也喜歡使用不同標(biāo)準(zhǔn)化的方法。不像把一套定義好的標(biāo)準(zhǔn)寫在紙上,他們更喜歡這樣的理念:創(chuàng)造有用的工具,其他的開發(fā)者也可以用來解決他們遇到的類似問題。這些工具從實(shí)現(xiàn)代碼中收集而來,并分享給更廣泛的團(tuán)隊(duì)。有時(shí)會(huì)以但不限于內(nèi)部開源的方式使用。如今git和github已經(jīng)成為版本控制系統(tǒng)的事實(shí)上的選擇,在公司內(nèi)部的開源實(shí)踐已經(jīng)變得越來越普遍了。
Netflix是奉行這種哲學(xué)的組織的一個(gè)好例子。分享有用的,重要的是經(jīng)過實(shí)戰(zhàn)驗(yàn)證的代碼庫會(huì)鼓勵(lì)其他開發(fā)者以類似的方式解決相似的問題,然而,如果需要也可以選擇不同的方法。這些共享庫傾向于解決數(shù)據(jù)存儲(chǔ)和進(jìn)程間通信等公共問題,也包括下面我們要進(jìn)一步討論的基礎(chǔ)架構(gòu)自動(dòng)化。
對于微服務(wù)社區(qū)來說,(對服務(wù)契約的)日常管理(overhead)尤其不受青睞。這不是說,社區(qū)不認(rèn)為服務(wù)契約具有價(jià)值。恰恰相反,往往是因?yàn)檎J(rèn)為它們更具價(jià)值,所以,他們在尋找管理這些契約的不同方法。如Tolerant Reader和消費(fèi)者驅(qū)動(dòng)型契約模式經(jīng)常應(yīng)用到微服務(wù)。這些模式有助于服務(wù)契約獨(dú)立進(jìn)化。作為構(gòu)建的一部分,執(zhí)行消費(fèi)者驅(qū)動(dòng)型契約,可以增加你的信心,為你的服務(wù)是否工作提供快速反饋。實(shí)際上,我們知道有一家澳大利亞的團(tuán)隊(duì)使用消費(fèi)者驅(qū)動(dòng)契約來驅(qū)動(dòng)構(gòu)建新的服務(wù)。他們使用簡單的工具來定義服務(wù)契約。這成為新服務(wù)在編碼之前的自動(dòng)構(gòu)建的一部分。隨后服務(wù)實(shí)現(xiàn)僅需要滿足這些契約就可以。這是一種優(yōu)雅的方法,以此可以避免構(gòu)建新的軟件時(shí)遇到“YAGNI”的困境。這些技術(shù)和圍繞這些技術(shù)出現(xiàn)的工具降低了服務(wù)間一時(shí)的耦合,減少了中心化契約管理的需求。
也許去中心化管治的最高境界就是亞馬遜所宣揚(yáng)的“構(gòu)建并運(yùn)行它”的理念。團(tuán)隊(duì)對構(gòu)建的軟件的方方面面負(fù)責(zé),包括7X24小時(shí)運(yùn)維。這種級(jí)別職責(zé)的下放絕對不是慣常做法,但我們確實(shí)看到越來越多的公司把這些職責(zé)賦予開發(fā)團(tuán)隊(duì)。Netflix是采用這種理念的組織。被自己寫的頁面凌晨3點(diǎn)喊醒必然促使你關(guān)注自己代碼的質(zhì)量。傳統(tǒng)中心化管治模式與這種思想?yún)s相去甚遠(yuǎn)。
去中心化數(shù)據(jù)管理
去中心化數(shù)據(jù)管理以許多不同的方式存在。從最高的抽象級(jí)別來看,它意味著世界的概念模型在不同的系統(tǒng)之間是不同的。當(dāng)在大型企業(yè)集成時(shí),這是一個(gè)常見的問題。客戶銷售試圖將會(huì)與(系統(tǒng))支持視圖不同。銷售視圖中所謂的客戶可能根本就不會(huì)出現(xiàn)在(系統(tǒng))支持視圖里。即使出現(xiàn)夜可能具有不同的屬性,或者(更糟糕的)屬性相同,但語義卻有細(xì)微的差別。
這種情況在不同的應(yīng)用之間是常見,但也可能出現(xiàn)在一個(gè)應(yīng)用的內(nèi)部,特別是當(dāng)應(yīng)用被拆分成單獨(dú)的的組件的時(shí)候。考慮此問題的一個(gè)有用的方式是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)中的界限上下文概念。DDD將一個(gè)復(fù)雜的域分解成多個(gè)有邊界的上下文,并在它們之間建立映射關(guān)系。這個(gè)過程對建立單體應(yīng)用和微服務(wù)架構(gòu)應(yīng)用都是有用的,但服務(wù)和使其更清晰的上下文邊界之間確實(shí)存在自然的關(guān)聯(lián),而且像我們在業(yè)務(wù)功能那節(jié)討論的一樣這種關(guān)聯(lián)得以增強(qiáng)。(注:服務(wù)是按照有上下文邊界的業(yè)務(wù)面來劃分的,所以服務(wù)邊界自然清晰。)
除了概念模型去中心化,微服務(wù)的數(shù)據(jù)存儲(chǔ)也可以去中心化。單體應(yīng)用偏向于使用單一邏輯數(shù)據(jù)庫實(shí)現(xiàn)數(shù)據(jù)持久化,企業(yè)通常也偏向于所有應(yīng)用使用一個(gè)單一的數(shù)據(jù)庫——許多這些決定是基于供應(yīng)商的商業(yè)許可模式做出的。微服務(wù)則偏好于讓每個(gè)服務(wù)管理它自己的數(shù)據(jù)庫,可以是相同數(shù)據(jù)庫的不同實(shí)例,也可以是完全不同的數(shù)據(jù)庫——這種方法叫作“復(fù)式持久化”(Polyglot Persistence)。你可以在單體應(yīng)用中使用這種數(shù)據(jù)持久化方法,但微服務(wù)中使用更多。

跨微服務(wù)的數(shù)據(jù)職責(zé)去中心化也意味著數(shù)據(jù)更新的管理。處理數(shù)據(jù)更新的常用方法就是要使用事務(wù)保證多個(gè)資源的一致性。這個(gè)方法經(jīng)常在單體應(yīng)用中使用。
這樣使用事務(wù)有助于保持一致性,但產(chǎn)生臨時(shí)耦合,這在跨多個(gè)服務(wù)時(shí)是有問題的。眾所周知,分布式事務(wù)是不易于實(shí)現(xiàn)的。作為微服務(wù)架構(gòu)的一個(gè)結(jié)果,就是強(qiáng)調(diào)服務(wù)間無事務(wù)協(xié)作。作為一個(gè)明確的共識(shí)就是,一致性僅僅是最終一致性,中間出現(xiàn)的問題由一些補(bǔ)償操作來處理。
選擇以這種方式來管理不一致性對許多開發(fā)團(tuán)隊(duì)來說是一個(gè)挑戰(zhàn),但它往往和業(yè)務(wù)實(shí)踐是相似的。業(yè)務(wù)常常會(huì)運(yùn)用一定程度的不一致性來快速響應(yīng)需求,而使用某種逆向過程來應(yīng)對錯(cuò)誤(注:如事務(wù)回滾)。只要修正錯(cuò)誤的成本小于保持更高一致性情況下業(yè)務(wù)損失的成本,這種權(quán)衡之計(jì)就是值得的。
基礎(chǔ)架構(gòu)自動(dòng)化
基礎(chǔ)架構(gòu)自動(dòng)化技術(shù)在過去的幾年取得了極大的進(jìn)步——隨著云計(jì)算的發(fā)展,尤其是AWS,其從構(gòu)建、部署到運(yùn)維微服務(wù)的復(fù)雜度都得到了降低。
許多使用微服務(wù)架構(gòu)的產(chǎn)品和系統(tǒng)都是由在持續(xù)部署以及它的前導(dǎo)持續(xù)集成方面經(jīng)驗(yàn)豐富的團(tuán)隊(duì)構(gòu)建。使用這種方式構(gòu)建軟件的團(tuán)隊(duì)廣泛使用基礎(chǔ)架構(gòu)自動(dòng)化技術(shù)。下圖描述了一個(gè)構(gòu)建管道。

由于這里不是介紹持續(xù)發(fā)布的文章,所以我們只在這里關(guān)注下幾個(gè)關(guān)鍵特性。 我們想獲得盡可能多的自信,我們的軟件可以工作,為此我們運(yùn)行許多自動(dòng)化測試。 把工作的軟件送入這個(gè)構(gòu)建管道就意味著自動(dòng)部署到新的環(huán)境。
單體應(yīng)用將被相當(dāng)愉快的構(gòu)建、測試以及通過這些環(huán)境(注:如上圖所示,集成測試環(huán)境、UAT環(huán)境和性能測試環(huán)境)。事實(shí)證明一旦你投資部署單一應(yīng)用的到生產(chǎn)環(huán)境的管道自動(dòng)化,那么部署更多的應(yīng)用似乎不再那么恐怖。記住,持續(xù)部署的目的之一就是使得部署過程變得無趣,所以,不管一個(gè)還是三個(gè)應(yīng)用程序,只要部署過程仍然是無趣,那就無所謂。
另外一個(gè)我們看到團(tuán)隊(duì)大量使用基礎(chǔ)架構(gòu)自動(dòng)化的領(lǐng)域是生產(chǎn)環(huán)境的微服務(wù)管理。我們上面斷言只要部署是無聊的,那么單體和微服務(wù)部署沒什么不同,但相反,各自運(yùn)維視覺完全不同。

容錯(cuò)設(shè)計(jì)
使用服務(wù)作為組件的一個(gè)結(jié)果是應(yīng)用程序需要設(shè)計(jì)能夠容忍服務(wù)的失敗。任何服務(wù)的調(diào)用都有可能因?yàn)榉?wù)無效而失敗??蛻舳诵枰M可能優(yōu)雅的應(yīng)對此情況。這相比單體設(shè)計(jì)是一個(gè)缺點(diǎn),因?yàn)樗鼮榱颂幚矸?wù)調(diào)用失敗而引入額外的復(fù)雜性。這也導(dǎo)致微服務(wù)團(tuán)隊(duì)不斷的考量服務(wù)的失敗如何影響用戶的體驗(yàn)。Netflix的Simian Army在工作日引發(fā)服務(wù)甚至數(shù)據(jù)中心故障來測試應(yīng)用程序彈性和對其監(jiān)控。
這種生產(chǎn)環(huán)境的自動(dòng)化測試足夠讓大多數(shù)運(yùn)維團(tuán)隊(duì)在開始休假一周前就趕到膽戰(zhàn)心驚。這不是說單體架構(gòu)風(fēng)格不能做復(fù)雜的監(jiān)控設(shè)置,只是在我們所經(jīng)歷的里頭比較少見。
因?yàn)榉?wù)可能在任何時(shí)候都會(huì)發(fā)生故障,所以迅速的發(fā)現(xiàn)故障,如果可能,自動(dòng)恢復(fù)服務(wù)就顯得很重要了。微服務(wù)應(yīng)用非常重視監(jiān)控,包括架構(gòu)層面的指標(biāo)監(jiān)控(比如,每秒有多少讀數(shù)據(jù)庫的請求(rps))和業(yè)務(wù)相關(guān)的指標(biāo)監(jiān)控(比如,每分鐘收到多少訂單)。語義上的監(jiān)控可以提供一個(gè)警報(bào)系統(tǒng),顯示什么情況正在變得糟糕,從而驅(qū)動(dòng)開發(fā)團(tuán)隊(duì)進(jìn)行跟進(jìn)和調(diào)查。
這對微服務(wù)架構(gòu)尤其重要,因?yàn)槲⒎?wù)對編排和“事件協(xié)作”的偏好可能會(huì)導(dǎo)致意外的行為。雖然許多權(quán)威專家盛贊意外收獲的價(jià)值,但事實(shí)上,意外行為有時(shí)候可能是壞事。監(jiān)控對迅速發(fā)現(xiàn)意外情況并處理至關(guān)重要。
單體應(yīng)用可以和微服務(wù)一樣透明的構(gòu)建——事實(shí)上也應(yīng)該這樣。不同的是,你必須要知道運(yùn)行在不同的進(jìn)程里的服務(wù)什么時(shí)候連接斷開了。但這種透明對于同一個(gè)進(jìn)程里頭的庫來說基本沒什么意義。
微服務(wù)團(tuán)隊(duì)期待看到對每個(gè)獨(dú)立的微服務(wù)的細(xì)致的監(jiān)控和日志設(shè)置,比如顯示開關(guān)狀態(tài)和各種運(yùn)維及業(yè)務(wù)相關(guān)指標(biāo)的儀表盤。另外像我們?nèi)粘S龅降睦?,如關(guān)于斷路器,當(dāng)前吞吐量和延遲的詳細(xì)信息。
演進(jìn)式設(shè)計(jì)
微服務(wù)的實(shí)踐者通常都具有演進(jìn)式設(shè)計(jì)的背景,把服務(wù)分解看作是一個(gè)長遠(yuǎn)的工具,幫助應(yīng)用程序開發(fā)者在不減緩應(yīng)用程序的變化速度的同時(shí)控制變化。控制變化不是必須抑制變化——有正確的態(tài)度,好的工具,你能夠使得軟件頻繁快速的變化,并使其得到良好的控制。
無論你什么時(shí)候想把軟件系統(tǒng)拆分成組件,你都會(huì)面臨如何拆分的問題——我們應(yīng)該遵循什么原則來切分我們的應(yīng)用? 組件的一個(gè)關(guān)鍵屬性是關(guān)于獨(dú)立替換和可升級(jí)的概念——這意味著,我們要尋找一個(gè)點(diǎn),我們可以想象,在這個(gè)點(diǎn)的代碼可以重寫但不會(huì)影響它的協(xié)作者。實(shí)際上,許多微服務(wù)團(tuán)隊(duì)考慮的更多,他們明確的期望許多服務(wù)可以被廢棄而不是長期演進(jìn)(注:也就是這個(gè)服務(wù)不僅可以獨(dú)立重寫,替換,甚至可以拋棄而不會(huì)影響其他的部分)。
Guadian網(wǎng)站是一個(gè)很好的例子,它被設(shè)計(jì)和構(gòu)建成了一個(gè)單體系統(tǒng),但在朝微服務(wù)方向演進(jìn)。單體仍然是這個(gè)網(wǎng)站的核心。不過他們更喜歡使用微服務(wù)增加新的功能,這些微服務(wù)調(diào)用單體的API. 這種方法對于提供本質(zhì)上是臨時(shí)性的功能,比如體育賽事相關(guān)的頁面尤其便利。作為網(wǎng)站一部分的這種功能可以使用快速開發(fā)語言迅速的組織在一起,一旦賽事結(jié)束就移除掉。我們在金融機(jī)構(gòu)也看到了類似的處理,一個(gè)新的服務(wù)因市場機(jī)遇被創(chuàng)建加入系統(tǒng),幾個(gè)月甚或幾周后就撤掉。
強(qiáng)調(diào)可替換性是更通用的模塊化設(shè)計(jì)原則的一個(gè)特例,通過變化模式驅(qū)動(dòng)模塊化(注:Kent Beck在《Implementation Patterns》中提到的變更速率原則——變更速率相同的數(shù)據(jù)和邏輯放在一塊,而不同的要分開)。你要把在同一時(shí)間變化的東西放在同一個(gè)模塊里。很少變化的系統(tǒng)部分應(yīng)該和當(dāng)前不斷變化的部分處于不同的服務(wù)里。假如你發(fā)現(xiàn)自己在不斷重復(fù)的同時(shí)修改兩個(gè)服務(wù),那么意味著這兩個(gè)服務(wù)應(yīng)該合并。
以服務(wù)的方式構(gòu)建組件使得可以制定更加細(xì)粒度的發(fā)布計(jì)劃。單體應(yīng)用的任何變更要求整個(gè)應(yīng)用的完全重新構(gòu)建和部署。然而,使用微服務(wù),你只需要重新部署你修改的服務(wù)。 這可以簡化并加速發(fā)布過程。確定就是你不得不關(guān)心對服務(wù)的變更是否會(huì)影響它的使用者。傳統(tǒng)的集成使用版本化來應(yīng)對這個(gè)問題。但是在微服務(wù)的世界里更偏向于把版本化作為最后的手段。我們可以把服務(wù)設(shè)計(jì)對它所以依賴的服務(wù)的變化盡可能的容錯(cuò)來避免大量的版本化。
微服務(wù)是未來嗎?
撰寫該文的主要目的是闡述微服務(wù)的主要思想和原則。這同時(shí)讓我們更清楚的認(rèn)識(shí)到微服務(wù)架構(gòu)風(fēng)格是一個(gè)重要的思想——值得為企業(yè)級(jí)應(yīng)用認(rèn)真考慮。我們目前構(gòu)建了數(shù)個(gè)此類風(fēng)格的系統(tǒng),也知道一些公司在使用和喜好這種構(gòu)建服務(wù)的方法。
我們了解在某種程度上作為推動(dòng)這種架構(gòu)風(fēng)格的先鋒包括:亞馬遜、Netflix、Guardian、英國政府?dāng)?shù)字化服務(wù)、realestate.com.au、 前進(jìn)報(bào)(Forward)以及comparethemarket.com。 2013的巡回大會(huì)上有很多轉(zhuǎn)向使用類似微服務(wù)的如Travis CI的東西的公司的例子。同時(shí),也有許多組織長期在做的事情,我們把它們歸類為微服務(wù),只是沒有叫這個(gè)名字而已(通常會(huì)打上SOA的標(biāo)簽——盡管,如我們所說,SOA以很多相互矛盾形式存在)。
然而,盡管有這些積極的經(jīng)驗(yàn),但我們并不確定微服務(wù)就是未來軟件架構(gòu)的方向。雖然到目前為止我們所遇到的(微服務(wù)架構(gòu)方面的情況)相對于單體應(yīng)用來說都是正面的,但我們也清楚這樣一個(gè)事實(shí):那就是我們所以經(jīng)歷的時(shí)間還是不夠,以至于無法做出處充分的判斷。
往往,只有在你做出了架構(gòu)決策幾年后其真正的結(jié)果才足以顯現(xiàn)出來(注:證明當(dāng)初決策是對還是錯(cuò))。我們看到的項(xiàng)目,有一個(gè)好的團(tuán)隊(duì),帶有很強(qiáng)的模塊化意念,可構(gòu)建的單體架構(gòu)多年來已經(jīng)腐朽了(注:模塊化邊界變得模糊,耦合度升高,內(nèi)聚度降低)。許多人認(rèn)為這種腐朽在微服務(wù)里頭是幾乎不可能發(fā)生的,應(yīng)為服務(wù)的邊界是清晰的,難以遮掩的。然而,除非我們看到足夠多經(jīng)年的系統(tǒng),否則我們無法真正評(píng)估微服務(wù)架構(gòu)成熟度。
當(dāng)然,人們有理由預(yù)料到微服務(wù)不會(huì)成熟的很好。對于組件化的任何努力,其成功在于如何使得組件很好的適配軟件。準(zhǔn)確的找出組件的邊界應(yīng)該位于哪里是很困難的。演化式設(shè)計(jì)意識(shí)到正確獲取邊界的困難和重構(gòu)的重要性。但是當(dāng)你的組件服務(wù)是使用遠(yuǎn)程通信時(shí),重構(gòu)它們會(huì)比重構(gòu)進(jìn)程內(nèi)的庫更加困難:代碼跨服務(wù)邊界移動(dòng)是困難的,接口的變更需要參與者調(diào)整,需要增加向后兼容層,測試也會(huì)變得更加復(fù)雜。
另外一個(gè)問題就是假如你沒有清晰的設(shè)計(jì)好組件,那么你所做的其實(shí)就是把復(fù)雜性從組件內(nèi)部轉(zhuǎn)移到了組件之間的連接上。這不僅僅把復(fù)雜性轉(zhuǎn)移了,而且還轉(zhuǎn)移到了模糊及難以控制的地方。當(dāng)看著小且簡單的組件內(nèi)部,而且組件間也沒有繁雜的連接,那么你很容易相信事情會(huì)變得更好。
最后,團(tuán)隊(duì)技能因素。新的技術(shù)易于被技能更好的團(tuán)隊(duì)采用。 但在高技能的團(tuán)隊(duì)使用的高效技術(shù)在低技能團(tuán)隊(duì)不一定湊效。我們見過大量的例子,低技能團(tuán)隊(duì)構(gòu)建混亂的單體架構(gòu)。不過要知道這種混亂發(fā)生在微服務(wù)架構(gòu)里會(huì)出現(xiàn)什么情還需要時(shí)間。糟糕的團(tuán)隊(duì)總是創(chuàng)建糟糕的系統(tǒng)——很難判斷微服務(wù)是否減少了這種情況下的混亂,還是使它變得更糟。
我們聽到的一個(gè)合理的觀點(diǎn)是你一開始不應(yīng)該使用微服務(wù)架構(gòu),而是單體架構(gòu),同時(shí)保持模塊化, 然后一旦單體變得有問題時(shí)拆分成微服務(wù)。(雖然這個(gè)建議不是很理想,因?yàn)楹玫倪M(jìn)程間接口通常并不一定是好的服務(wù)接口。)
所以我們謹(jǐn)慎樂觀的說:到目前為止,根據(jù)看到的足夠多的關(guān)于微服務(wù)的信息,我們認(rèn)為微服務(wù)是一個(gè)值得嘗試的方向。我們不能確定最終如何,但軟件開發(fā)的挑戰(zhàn)之一就是你只能僅僅憑你目前能獲取的不完善的信息來做出決定。