請不要在“微服務”的狂熱中迷失自我!

AskHarries發表於2018-06-25

微服務在過去幾年一直是一個非常熱門的話題(附錄1)。何為“微服務的瘋狂”,舉個例子:

眾所周知,Netflix在DevOps上的表現非常棒。Netfix可以做微服務。因此:如果我做微服務,我也將非常擅長DevOps。

很多情況下,我們盲目的投入巨大的努力來接入微服務模式,然而往往卻很少去考慮接入的成本和收益能否有效的幫我們解決當前我們正面臨的痛點。

下面,我將詳細描述微服務是什麼,以及為什麼這種模式這麼吸引人,最後再聊一聊一些微服務正面臨的一些關鍵挑戰。

如果你正在考慮微服務是否適合你,是否能幫你解決當前面臨的問題?那繼續往下看,我會用一系列簡單的問題來幫你走出你的困惑。這一系列“問題”在文章的最後。
?wx_fmt=png

什麼是微服務,為什麼微服務如此受歡迎?

來來來,老司機帶你從基礎走一波。一個例子:下面這張圖是一個假想的視訊共享平臺的實現方式,左側是用一個“整體服務”來實現,右側是多個微服務的形式來實現:
?wx_fmt=png
兩種系統設計的區別在於左側是一個整體的大而全的服務。右側是一組小而多,但每個都是一個具體的服務,每個服務都有特定的角色。

當從系統細節層面來繪製圖表時,很容易看出微服務的很多潛在的好處,簡單從幾個方面來說一下:

獨立開發:小型獨立元件可由小型獨立團隊構建。一個小組可以改變“Upload”服務,而不會干擾“Transcode”服務,甚至都不需要知道這個服務。瞭解元件的時間大大減少,開發新功能也更容易。

獨立部署:每個單獨的元件都可以獨立部署。這樣可以以更快的速度和更少的風險釋出新功能。比如:“Streaming”元件的修復或功能可以部署,而不需要部署其他元件。

獨立的可伸縮性:每個元件可以彼此獨立地進行縮放。在需求多併發同時需要釋出新的版本時,可以放大“Download”元件,以處理增加的負載,而不必擴大每個元件,這使得彈性縮放更加可行並降低了成本。

可重用性:元件實現一個小的,特定的功能。這意味著它們可以更容易地適用於其他系統、服務或產品。“Transcode”元件可以被其他業務部門使用,甚至可以變成一個新的業務,或者為其他組提供Transcode服務。

從細節層面上來看,微服務模型較之於整體模型的好處顯而易見。但是問題來了,如果真的是這樣的話 – 為什麼微服務模式最近幾年才流行?我一生都快走完了(尷尬,貌似暴露年齡了),它才出現?

如果這微服務這麼好,為什麼之前大家沒有這麼做呢?

這個問題有兩種答案。其一是,它強依賴我們最好的技術能力,另一個是最近的技術進步,促使我們能夠把它帶到一個新的高度。

當我開始寫軟文來回答這個問題的時候,發現這將會是一個很長的描述,所以從實際的角度出發,我將把它拆成兩篇文章,稍後再發表2。第1篇文章,我將跳過一些內容,比如:從單個程式到多個程式的過程,忽略ESB和麵向服務的體系結構,元件設計和有限的上下文等等。

感興趣的朋友可以稍後閱讀更多關於journey的資訊。儘管,在很多方面我們已經這樣做了一段時間,但是隨著最近容器技術(特別是Docker)和編排技術(如Kubernetes、Mesos、Consul等等)的普及,從技術的角度來看,微服務模式變得更加可行。

因此,如果我們想要實施微服務的話,我們最好仔細慎重考慮是否真的需要。我們已經看到了高大上的“理論效益”,但值得一提的是,未知的挑戰又是什麼?

微服務有什麼問題呢?

微服務如此強大、完美,哪裡還會有什麼挑戰?這是目前我見過的最大的問題。

開發的複雜性增加

對於開發者來說事情會變得更加困難。在開發人員想要在遠端工作的情況下,或者可能跨越許多服務的功能的情況下,開發人員必須在他們的機器上執行它們,或者連線到它們。這通常比簡單地執行單個程式更復雜。

這個挑戰可以通過工具(附錄3)得到部分緩解,但隨著構成系統的服務數量的增加,開發人員在整個系統執行時面臨的挑戰也會越來越多。

運維的複雜性增加

對於不開發服務但維護服務的團隊來說,潛在的複雜性是一個巨大的挑戰。他們不是管理幾個正在執行的服務,而是管理數十,數百或數千個正在執行的服務。服務越多,溝通越多,潛在的失敗風險就越多。

DevOps的複雜性增加

閱讀以上兩點,可能會發現運維和開發是分開處理的,尤其是考慮到DevOps作為一種實踐的普及(我是DevOps的真愛粉)。DevOps難道不能緩解這一痛點?

目前面臨的挑戰是,許多組織仍然依靠獨立的開發和運營團隊來執行 – 而一些組織則更傾向於採用微服務。

對於已經採用了DevOps的組織來說,這仍然很難。既是開發者又是運維者,已經非常艱難(但是要建立好的軟體卻很關鍵),但是也必須瞭解容器編排系統的細微差別,特別是快速發展的系統是非常困難的。這使我想到了下一點。

沒專業知識?別玩微服務

當很多事情都由專家完成時,最終的結果也將是極好的。但想象一下,一個機構或組織使用單一的整體系統並不總是可以很順利的執行。那做些什麼能夠來改善並讓這些事情變得更好呢?通過增加系統服務的數量?但同時也會增加執行的複雜性。

不可否認,通過有效的自動化、監控和編排等,這一切都可以改善。但挑戰很少是技術本身——真正的挑戰其實是找到能夠有效使用技術的人。恰恰目前這些技能需求非常高,可能很難找到符合你需求的人。

現實世界的系統往往界限不清

在我們用來描述微服務的好處的所有例子中,我們都談到了獨立的元件。但是在很多情況下,元件並不是完全獨立的。正所謂“紙上得來終覺淺,絕知此事要躬行”,某些領域可能看起來有限,但是當你陷入冗繁的細節時,你會發現他們比你預期的更具挑戰性。

這是事情變得非常複雜的地方。事實上,如果你的邊界沒有明確定義,那麼會發生什麼情況呢?即使理論上的服務可以單獨部署,你會發現,由於服務之間的相互依賴關係,你必須部署一系列微服務作為一個組服務。

這意味著你需要管理協同工作的版本,這些版本的服務在聯調時會經過驗證和測試,你實際上沒有可獨立部署的系統,因為要部署新功能,你需要仔細編排許多服務的同時去部署。

狀態的複雜性往往被忽略

在前面的例子中,我提到一個功能部署可能需要同時部署多個版本的許多服務。假設合理的部署技術將緩解這種情況,例如藍/綠部署(大多數服務編排平臺很少原生支援這種功能),或者並行執行多個版本的服務,以及決定使用哪個版本的消費通道。

如果服務是無狀態的,這些技術可以緩解大量的挑戰。但是無國界的服務非常坦率,容易處理。事實上,如果你有無狀態的服務,那麼我會傾向於考慮跳過微服務,並考慮使用無伺服器模型。

實際上,許多服務需要管理。我們的視訊共享平臺的一個例子可能是訂閱服務。訂閱服務的新版本可以以不同形狀將資料儲存在訂閱資料庫中。如果你同時執行這兩個服務,則一次執行兩個模式的系統。如果您進行了藍/綠部署,而其他服務依賴於新形狀中的資料,則必須同時更新這些資料,並且如果訂閱服務部署失敗並回滾,則可能還需要使用級聯回滾。

同樣,可能你會說,在NoSQL資料庫中,這些架構問題會消失,但事實並非如此。不強制執行模式的資料庫無法連線無模式系統——這意味著模式往往是在應用程式級而不是資料庫級進行管理的。理解資料結構以及如何流轉的根本性問題並不能被消除。

溝通的複雜性往往被忽略

當你建立一個相互依賴的大型服務網路時,可能會有很多的服務間通訊。這導致了一些挑戰。首先,有很多事情可能會失敗。我們必須假設網路call可能會失敗,這意味著當一個服務call另一個服務時,它應該至少需要重試幾次。現在當一個服務可能呼叫很多服務時,我們最終會遇到一個更加複雜的情況。

使用者上傳視訊共享服務中的視訊。我們可能需要執行upload服務,將資料傳遞到transcode服務,更新訂閱,更新建議等等。所有這些呼叫都需要一定程度的協調,如果過程中任何部分失敗,我們都需要重試。

這個重試邏輯可能難以管理。試圖同步做事往往會導致站不住腳,失敗點太多。在這種情況下,更可靠的解決方案是使用非同步模式來處理通訊。這裡面臨的挑戰是非同步模式本身往往會使系統具有狀態性。如前所述,分散式狀態系統和有狀態系統很難處理。

當一個微服務系統使用訊息佇列進行服務內通訊時,你基本上需要有一個大的資料庫(訊息佇列或代理)將這些服務組合在一起。同樣,雖然起初看起來似乎不是一個挑戰,但你懂的——出來混遲早都是要還的。X版本的服務可能會寫入某種格式的訊息,當傳送服務更改傳送的訊息的詳細資訊時,依賴於該訊息的服務也將需要更新。

當然,可以有許多不同格式的訊息處理服務,但這很難管理。現在,在部署新版本的服務時,你可能會有兩個不同版本的服務嘗試處理來自同一佇列的訊息,甚至可能是由不同版本的傳送服務傳送的訊息。這可能會導致複雜的邊緣情況。為了避免這些邊緣情況,僅允許特定版本的訊息存在可能更容易,這意味著你需要將一組服務的版本作為一個整體來部署,以確保先前版本的訊息被正確地遮蔽。

這再次突出表明,獨立部署的想法可能不會像預期的那樣順利。

版本控制可能很難

為了緩解前面提到的挑戰,版本控制需要非常謹慎的管理。再說一下,看起來貌似有一種趨勢——假設遵循像Semver[4]這樣的標準或許將可以解決這個問題。然而事實並非完全如此。雖然Semver是一個合理的使用慣例,但是你仍然需要持續的跟蹤那些可以一起工作的服務和API的版本。

這可能會使事情變得非常具有挑戰性,並且很多時候可能會讓你感到困惑——哪些版本的服務可以一起正常工作。

在軟體系統中管理依賴關係是非常困難的,無論是節點模組,Java模組,C庫還是其他。當一個實體消費獨立元件之間的衝突的挑戰是很難處理的。

當依賴關係是靜態的時候,這些挑戰是很難處理的。雖然可以進行修補、更新、編輯等,但是如果依賴關係本身是實時服務,那麼你可能根本無法更新它們——你可能需要執行許多版本(上面已經描述過這些挑戰),或者直到整個系統得到修復。

分散式事務

在需要跨操作交易完整性的情況下,微服務可能會非常痛苦。分散式狀態很難處理,很多小的單位可能會很難進行編排交易。

試圖通過使操作冪等性,提供重試機制等來避免這個問題可能聽起來很誘人,而且在很多情況下確實可能起作用。但可能有一些場景,你只需要一個事務失敗或成功,而不想它處於中間狀態。解決這個問題或者在微服務模型中實現它的代價可能是非常高的。

微服務可能是變相的龐然大物

顯然,單獨的服務和元件可能是孤立部署的,但是在大多數情況下,你將不得不執行某種編排平臺,比如Kubernetes。如果你使用的是託管服務,例如Google的GKE 5或Amazon的EKS 6,則會為你處理管理群集的大量複雜性。

但是,如果你要自己管理叢集,那麼你正在管理一個龐大而複雜的關鍵任務系統。儘管單個服務可能具有前面所述的所有優點,但你需要非常小心地管理群集。這個系統的部署可能很難,更新可能很難,故障轉移可能也很困難等等。

在許多情況下,總體收益仍然存在,但重要的是不要輕視或低估管理另一個龐大而複雜系統的額外複雜性。託管服務可能會有所幫助,但在很多情況下,這些服務都是新興的不穩定的(例如,Amazon EKS直到在2017年底才宣佈)——誰用誰知道。

微服務瘋狂之死!

只有通過仔細考慮才能避免為微服務而微服務的瘋狂。為了幫助解決這個問題,我想了一些你可能想問自己的問題,以及可能的答案:
?wx_fmt=png
你可以在這裡下載PDF副本:https://github.com/dwmkerr/blog/raw/master/2018/microservice-madness/images/microservice-questions.pdf

最後的想法:不要混淆微服務和架構

我故意避免這篇文章中的“a”字。但是,我的朋友Zoltan在校對這篇文章的時候提到了一個很好的觀點。

沒有微服務體系結構。微服務只是元件的另一種模式或實現,無他。無論是否存在於系統中,都不意味著系統的體系結構得到了解決。

微服務在許多方面與打包和運維的技術過程有關,而不是系統的固有設計。元件的適當邊界仍然是工程系統中最重要的挑戰之一。

無論你的服務是否在Docker容器中,你總是需要仔細考慮如何將系統放在一起。沒有唯一的答案,只有更多的選擇。

我希望你看完這篇文章覺得有趣!一如既往,如果你有任何疑問或想法,請在下面評論即可。

附錄:進一步閱讀

以下連結可能有用:

  • Martin Fowler – Bounded Context – 馬丁的文章是很棒的,我極力推薦這一篇。
  • Martin Fowler – 微服務 – 這篇著重介紹微服務的模式。
  • 微服務 – 好還是壞? – 閱讀這篇文章,你將瞭解BjörnFrantzén對微服務的看法。
  • 什麼時候不要做微服務 – 來自於Christian Posta的話題中的優秀帖子
  • Dave Kerr -微服務整體架構 – 微服務世界中CI / CD和DevOps的實用技巧 – 最近一次關於微服務的devops會議演示。

如果你願意,請分享你認為值得閱讀或觀看的話題的任何東東!

參考

  1. https://trends.google.com/tren … rvice
  2. 如果你不想錯過這篇文章,你可以訂閱RSS源,或者在LinkedIn或Twitter上關注我。
  3. Docker Compose是一個很好的解決方案,Fuge非常聰明,在MiniKube這種情況下,也可以選擇在本地執行編排。
  4. https://semver.org/
  5. 谷歌Kubernetes引擎,一個從谷歌雲平臺誕生Kubernetes託管服務:https://cloud.google.com/kubernetes-engine/
  6. 亞馬遜彈性容器服務(已支援Kubernetes),一個從亞馬遜Web服務誕生的Kubernetes託管服務:https://aws.amazon.com/eks/
請不要在“微服務”的狂熱中迷失自我!


相關文章