很多開發團隊認為,對于整體架構來說,微服務架構風格是一種強有力的方式。但是其他團隊認為微服務架構存在明顯生產率的負擔。和其它架構風格一樣,微服務也有利弊。為了做出最好的選擇,你必須理解這些并且使用到你特定的上下文。
微服務提供的好處:
1.強模塊邊界:微服務強化了模塊結構,對于大型團隊尤其重要。
2.獨立部署:簡單的服務更容易部署,并且因為它們是自治的,當它們出現問題時極少可能導致系統故障。
3.技術多樣性:使用微服務你可以組合多種語言,開發框架和數據存儲技術。
但是微服務也有代價:
1.分布:分布式系統更難組織,因為遠程調用慢并且總是有失敗風險。
2.最終一致性:對于分布式系統保持強一致性是極其困難的,這意味著每一個人必須管理最終一致性。
3.運維復雜:你需要一個成熟的運維團隊來管理這么多經常要被重新部署的服務。
強模塊邊界
微服務第一個大的好處是強模塊邊界。這是一個重要還不可思議的好處,因為理論上沒有原因說明為什么一個微服務本應該比一個整體有更強的模塊邊界。
那我說的強模塊邊界是什么呢?我想大多數人可能同意一個觀點,把軟件劃分成多個模塊是好的:軟件的所有分塊都是相互解耦的。你要讓你的模塊工作已至于如果我需要改變系統中某個部分,大部分時間我只需要理解系統中的一小部分就可以修改,并且我發現那一小部分非常簡單。好的模塊結構在任何組織都是有用的,但是隨著軟件大小的成長它的重要性會指數式增加。可能更重要的是,隨著開發團隊的壯大,模塊結構變的更加重要。
微服務的擁護者很快提出Conways Law,它的概念是軟件系統的結構反映了構建此軟件的組織的溝通結構。對于更大的團隊,尤其是如果這些團隊在不同的地點,結構化軟件并且認識到團隊之間交流將會比在一個團隊內的交流更少和更正式是非常重要的。在這種交流模式下微服務允許每個團隊可以關心相對獨立的單元。
正如我早前說的,沒有原因說明為什么整體系統不應有好的模塊結構。但是很多人注意到這種情況似乎很少,因此 Big Ball of Mud是最普遍的架構模式。事實上正是整體結構的挫敗感驅使多個團隊轉向微服務。解耦的模塊可以工作因為模塊邊界成為模塊間引用的屏障。問題就是,在整體系統下很容易偷偷溜進邊界周圍。這樣做可能是快速實現功能的一個有用的技術捷徑,但是大范圍地使用破壞模塊結構,破壞團隊的生產率。把模塊放進獨立的服務使邊界更加堅固,使使用這些不好的解決方法更加困難。
耦合的一個重要的方面是持久化數據。微服務的主要特征之一是去中心化數據管理,它是說每一個服務管理自己的數據庫,任何其它服務必須通過服務的API獲取數據。這消除了集成數據庫,在一個大系統中它是令人討厭的耦合的一個主要地方。
鄭重強調一下整體結構非常有可能強化模塊邊界,但是它需要紀律。相似地你可能進入一個微服務的大泥球,但是它需要很大精力做一些錯事。以我看,使用微服務增加了你得到更好的模塊的可能性。如果你對你的團隊的紀律有信心,這可能消除了微服務的這個優勢,但是隨著團隊的成長,保持紀律漸漸地變的更加困難,正如維護模塊邊界變的更加重要一樣。
如果你得不到正確的邊界,這個優勢會變成一個障礙。對于Monolith First策略,這是兩個主要原因之一,也是為什么那些早期傾向于運轉微服務的人強調你只有在很好理解域的情況下使用微服務的兩個主要原因之一。
但是我仍然沒有在這一點上按照警告做。你可以僅僅完全地說明在一段時候過后一個系統是如何維護模塊的。所以當我們看到一個已經至少運行一些年的微服務系統,我們可以僅僅獲取是否微服務帶來了更好的模塊。此外,早期使用者變的更加有才華,所以在我們獲取一個由普通團隊寫的微服務系統的優勢時會有一些延遲。即使這樣,我們必須接受普通團隊寫的普通軟件,而不是和頂級團隊比較。我們必須與整體架構下的寫的軟件比較-這是一個巧妙的事實統計。
此時此刻,接下來是從我認識的使用這種風格的人那聽到的早期證據。他們的意見是對于維護他們的模塊,這種風格相當簡單。
一個案例研究特別有意思。這個團隊做了一個錯誤的決定,在一個不足夠復雜到包含Microservice Premium的系統上使用微服務。項目遇到了麻煩需要營救,所以更多的人投入到這個項目。此刻微服務架構變的有幫助,因為系統能夠承受開發人員的快速涌入,團隊能夠更容易的利用更多團隊成員相對于團隊在整塊系統的情況下。結果項目加快了比在整塊系統下期望的更多的生產率,使團隊趕上。結果仍然有一些凈負數的,軟件花費的員工工時大于在整體架構下花費的時間,但是微服務架構支持極速增加。
分布
微服務使用分布式系統來提高模塊化。但是分布式軟件有一個主要的劣勢,也正是它的分布式。一旦你要打分布式牌,你要遭受一大堆復雜性。我不認為微服務社區和分布式對象活動一樣關于它們的開銷是沒有經驗的,但是復雜性仍然存在。
這些復雜性中的一個是性能。很多天你必須在一個真正不尋常的點上觀察進程內函數調用變成一個性能瓶頸,但是遠程調用慢。如果你的服務調用了好幾次遠程服務,每一個遠程服務又好幾次調用其它遠程服務,這些響應時間意味著一些糟糕的延遲特征。
當然你可以做一些事情減輕這種問題。首先你可以增加你的調用粒度,以至于你使用更少的調用。這使你的編碼模型復雜化,現在你必須思考如何批處理你的內部服務間交互。它也只能帶你到這,因為你將要不得不至少調用每一個合作服務一次。
第二種減輕方法是使用異步。如果并發的使用6個異步,你會和最慢的調用一樣慢而不是它們延遲的和。這是一個大的性能優化,但是它帶來另一個認知成本。異步編程是很難的:很難寫對,同時更難調試。但是我聽到的大多數微服務例子使用異步為了獲得可接受的性能。
速度可靠之后,你期望進程內函數調用能夠工作,但是一個遠程調用可能在任何時候失敗。隨著更多微服務,會有更多潛在的失敗點。聰明的開發人員知道這個并且為失敗做設計。幸運地是為異步合作采取的各種措施也適用于處理失敗的情況并且結果能夠提高彈性。不是太多的補償,但是,你仍然有為每一個遠程調用執行失敗結論的復雜性事情。
這僅僅是分布式計算的前兩個謬論。
對這個問題有一些警告。首先很多這些問題隨著整體架構突然發生當它發展的時候。很少的整體架構是真正自包含的,常常有其它的系統,常常是遺留系統需要一同工作。與它們交互涉及到通過網路同時引用了這些相同的問題。這既是為什么很多人傾向于更快地轉移到微服務來處理與遠程系統同交互。這種問題也是一個經驗能夠幫忙的地方,一個更加熟練的團隊將更好的處理這種分布式問題。
但是分布式一直是一個成本。我一直勉強使用分布式,并且認為很多人很快使用分布式是因為他們低估了它的問題。
最終一致性
我確信你知道網站需要一點耐心。你更新一些內容,你的屏幕被刷新同時更新的內容沒有了。你等了一兩分鐘,點擊刷新,它出現了。
這是一個非常令人不快的可用性問題,幾乎確定是由于最終一致性危險造成的。你的更新被粉紅色節點接收,但是你的獲取請求被綠色節點處理。直到綠色節點從粉色節點得到它的更新,否則你是處在一個不一致的窗口。最終它將會一致,但是直到最終一致時,你一直疑惑是否那里出錯了。
像這種不一致情況特別令人不快,但是它們可能是非常重要的。業務邏輯可能根據不一致信息作出決定而結束,當這種情況發生時,診斷是哪里出錯將會極為困難,因為任何調查都是在一致窗口已經關閉很長時間之后發生。
微服務引入最終一致性內容由于它們值得贊賞的主張關于去中心化數據管理。使用整體架構,你可以在一個事務里更新一長串內容。微服務需要多個資源去更新,同時分布式事物很頭疼。所以現在,開發人員需要注意一致性問題,并且搞清楚當數據不同步時在代碼將要做任何回退之前如何檢測。
整體架構的世界對于這些問題也不自由。隨著系統的發展,有更多的需求需要使用緩存去提高性能,緩存失效是另一個問題。大部分應用使用離線鎖來避免長鏈接的數據庫事務。外部系統需要更新,這些更新不能與一個事務管理器協作。業務處理常常對不一致的容忍度比你認為的要強,因為業務常常更珍視使用性(業務過程有一種CAP定理的自然理解)。
和其它分布式問題一樣,整體架構不能完全避免一致性問題,但是它們遭受這種問題更少,尤其是當它們很小的時候。
獨立部署
對模塊邊界和分布式系統的復雜性的權衡充滿了我在這個領域的整個職業生涯。但是在過去的10年有一件事情明顯改變了,就是發布到產線的角色。在20世紀產品發布幾乎是一個痛苦又極少的事情,在周末的白天或者晚上將軟件有問題的部分切換到做有用事情的地方。但是現在,熟練的團隊頻繁的發布到產線,很多組織實踐持續交付,運行他們一天內做多次發布。
這種轉變已經對軟件工業有了深遠影響,它與微服務活動深深的纏繞在一起。多個微服務的成就都是被部署大的整塊架構應用的難題觸發的,在整體架構中一個小的改變都可能引起整個部署失敗。微服務的一個主要原則是服務是組件,可以獨立部署的。所以現在當你做了一個改變,你僅僅需要測試和部署一個小的服務。如果你把它弄亂了,你不用將整個系統停掉。畢竟,由于對失敗設計的需要,即使你的組件完全失敗也不應該阻止系統的其它部分工作,即使有一些優雅的降級處理。
這種關系是一種雙向道。隨著很多微服務需要頻繁發布,你的部署要一起進行是基本要求。這就是為什么快速應用部署和基礎的快速規定是微服務的前提條件。對于超出基本要求的任何事情,你需要進行持續交付。
持續交付的很大好處是從想法到可運行軟件的周期減少了。這樣做的阻止能夠快速應對市場變化,比他們的競爭對手更快地引用新功能。
盡管很多人引用持續交付作為使用微服務的一個原因,但是本質上一個大型的整體系統也能做到持續交付。Facebook和Etsy就是兩個最有名的例子。也有足夠多的嘗試微服務架構做獨立部署失敗的例子,多個服務需要小心合作發布。當我聽到很多人爭論使用微服務更容易進行持續交付,比起他們對于模塊化的實踐重要性,我更不堅信他們在微服務上的看法-盡管模塊化天然地與交付速度有很強的關聯。
運維復雜化
能夠快速部署一個小的獨立的單元對于部署來說是極好的,但是隨著5,6個應用轉成上百個小的微服務,它把額外的負擔放到了運維上。很多組織發現處理如此多的快速改變的工具的困難代價很高。
這強化了持續交付的重要作用。雖然持續交付對整體架構是一個可用的技能,幾乎一直要劃分精力才能得到,對于一系列微服務設置它是一種基本要求。沒有持續交付促進的自動化和合作,就沒有辦法處理10多個服務。由于對管理和監控這些服務的需求增加,運維復雜性也增加了。此外對于整體應用有用的成熟度級別,如果微服務在混合場景下,成熟度級別也是必須的。
處理這種運維復雜度需要一大堆新的技能和工具-特別強調在技能上。工具仍然不成熟,但是我的直覺告訴我即使使用更好的工具,在微服務環境下對技能的低障礙也是更高的。
到目前為止對于更好的技能和工具的需求不是處理這些運維復雜性的最難的部分。為了高效的做所有事情你也需要引入devops 文化:開發人員,運維和其它涉及到軟件交付的人之間更好合作。文化的改變是困難的,尤其在更大,更久的組織。如果你不能彌補技能和文化的改變,你的整體應用將會被阻礙,但是你的微服務應用也會痛苦。
技術多樣性
因為每一個微服務是獨立的部署單元,在每個微服務下你可以自由考慮你的技術選擇。微服務可以使用不同的語言,不能的庫和不同的數據存儲器。這允許團隊對于任務選擇合適的工具,某個語言和庫對一個特定類型的問題更合適。
技術多樣性的討論常常集中在對工作最好的工具上,但是微服務最大的好處是更普通的版本問題。在整體架構下你可能僅僅使用一個庫,一個地方的一個版本,這常常導致升級問題。系統的某個部分需要升級從而使用新功能但是不可以因為升級會打斷系統的其它部分。處理版本問題是這些問題中的一個,隨著代碼變大,這些問題會以指數級變得更難。
使用多種技術有風險,開發組織將會不知所措。我知道的大多數組織鼓勵有限的技術集合。這種鼓勵通過為諸如監控等提供基本的工具來支持,這使服務附著于一個公共環境的小文件上更加簡單。
不要低估支持實驗的價值。對于一個整體架構系統,早期對語言和框架的決定是很難被顛覆的。10年后或許這個決定把團隊鎖定到一個不方便的技術上。微服務允許團隊實踐新工具,漸漸地遷移系統一次一個服務。一個更強的技術變得非常明確。