不要從微服務開始!單體應用是你的朋友 - arnoldgalovics

banq發表於2021-12-17

我想寫下為什麼從一個全新的專案開始使用微服務通常是一個壞主意。

時機已到,這正是我將在本文中討論的內容。

微服務變得越來越自然,我們幾乎感覺自己一直生活在微服務的世界裡。最近,當我與其他開發人員交談並詢問他們將如何啟動綠地專案時,幾乎可以肯定,答案是,嗯,一個微服務用於此,另一個用於此,另一個用於使用者管理,另一個用於身份驗證,另一個用於授權,還有一個用於會話管理,列表可以繼續。

我將嘗試闡明沒有人在談論微服務時的內容。是的,這將是我過去參與的專案的第一手經驗。

 

謊言

我收集了其他文章提到的微服務的一些優點:

  • 故障隔離:

    由於應用程式由多個服務組成,如果一個服務出現故障或出現問題,只會影響系統的那一部分。想想 Netflix,當你在看節目時,你不在乎推薦。所以如果他們有一個服務來處理當前的觀看者併為他們提供視訊流;他們還有另一項服務來處理個人使用者推薦。如果推薦服務當機,他們系統中最重要的功能不會受到影響,即觀看節目。故障被隔離。

  • 消除技術鎖:

    想想單體:這是一個巨大的應用程式,擁有成百上千個 API,正在管理數百個資料庫表。該應用程式是用 Java 編寫的,該團隊花了過去 5 年的時間來開發它。一種奇特的新語言出現了,它在紙面上帶來了更好的效能,提供了更好的安全性,等等。這可能是 Go/Rust,團隊想要試驗該語言及其技術堆疊。他們如何在單體環境中做到這一點?他們不能,因為它是單個部署包。您可以 - 至少不容易 - 將應用程式的某些部分切換為不同的語言。對於微服務,您可以為不同的服務使用不同的技術堆疊。服務 A 可以用 Java 編寫,服務 B 可以用 Go 編寫,服務 C 可以用Whitespace編寫,如果你夠勇敢的話。

  • 更容易理解:

    當您有多個服務負責整體功能的一小部分時,服務本質上會更小,因此更容易理解。

  • 更快的部署:

    在常規的單體系統中,您要麼完全部署,要麼根本不部署。您需要部署一個包,這是一個全有或全無的方案。使用微服務,您有機會獨立部署,這意味著如果您需要部署推薦服務的升級(回到 Netflix 示例),您可以完全部署該單個服務並節省大量時間。

  • 擴充套件性

    您可以通過啟動多個例項來擴充套件您的服務,以增加特定功能的容量。就像前面的例子一樣,如果人們在 Netflix 上檢視大量推薦,他們可以輕鬆啟動推薦服務的多個例項來應對負載。在單體環境中,您要麼向上擴充套件應用程式的每個部分,要麼不擴充套件。

    現實生活中的微服務

 

現實生活中的微服務

  • 基礎設施要求

讓我從我在微服務方面遇到的最大困難之一開始:基礎設施

你有沒有部署過單體應用?當然,我們可以將其複雜化,但在常規情況下,如果您將其部署到雲中,這就是單體應用所需要的。讓我們以一個簡單的線上商店應用程式為例。

  • 應用程式的負載均衡器
  • 用於執行應用程式的計算例項
  • 應用程式的(關係)資料庫
  • Jenkins for CI(CD)
  • 用於日誌聚合的 Kibana

如果您要使用微服務:

  • Kubernetes 叢集
  • 負載均衡器
  • 用於執行應用程式和託管 K8S 叢集的多個計算例項
  • 一個或多個(關係)資料庫,取決於您是否要為每個服務使用一個資料庫
  • 用於服務-服務通訊的訊息系統,例如 Kafka
  • Jenkins for CI(CD)
  • 用於日誌聚合的 Kibana
  • 用於監控的 Prometheus
  • 用於分散式追蹤的 Jaeger/Zipkin

這只是一個高層次的概述。這應該是相當清楚的。微服務確實可以帶來價值,但問題是;以什麼代價?

儘管承諾聽起來非常好,但您的架構中有更多的移動部分,這自然會導致更多的失敗。如果您的訊息系統出現故障怎麼辦?如果您的 K8S 叢集出現問題怎麼辦?如果 Jaeger 當機而您無法追蹤錯誤怎麼辦?如果度量沒有進入 Prometheus 怎麼辦?

最初,您將花費更多時間(和金錢)來構建和操作這個複雜的系統。

(banq注:這是從上帝視角看複雜,這些是Complicate繁雜的,但是可以解決,因為每個團隊分別負責構建和維護這個Complex系統,否則單體就只是一個Complex複雜性系統,試圖依靠幾個人構建和維護一個單體複雜性系統,表面上OK,實際是自欺欺人,微服務將Complex複雜性變成Complicated系統,作者可能沒有上升到這種認知,以下基本都是從這種上帝視角的認知級別看問題,所以得出單體比微服務好的結論)

 

更快的部署?

我將觸及優勢列表中的第一點;更快的部署。當你想到 Netflix、Facebook、Twitter 並觀看他們的會議演講時,他們描述了他們正在執行的微服務的數量,以及他們如何向 Git 提交一些東西,並且在幾個小時內它就會投入生產。是不是好得令人難以置信?

在我看來這絕對是可以實現的,但我承認我從未參與過這樣的微服務專案。我並不是說這是不可能的,從穩定性、基礎設施和文化的角度來看,這真的很難做到。

讓我分享一下這通常是如何從我的經驗中得出的。在對綠地專案進行一行編碼之前,您通常會先進行一些探索,即如何將產品轉化為技術解決方案。你設計系統,你設計你的微服務,有多少人會在那裡,責任等等。

一個真正的教育專案是我們進行這項練習的地方,我們最終在 4 個月內完成了 80 多個微服務?

這 80 多個微服務在現實中的含義是,與將 80 多個微服務放在一個整體中並進行部署相比,我們絕對可以更快地部署單個服務,但是……

80 多個微服務太小了,以至於一個單獨的開發單元——敏捷世界中的故事——永遠無法只涉及一個服務。該系統從根本上被搞砸了,更快部署的承諾立即消失了。我們沒有更快的部署,而是相反,更慢的部署。慢得多。

另外,我會多次反思這一點。部署期間更多的移動部件意味著更多的潛在故障。很多時候,基礎設施不夠穩定,部署隨機失敗,因為

  • 下載/上傳軟體包時,Artifactory/Nexus/Docker 儲存庫在極短的時間內不可用
  • Jenkins builder 隨機卡住了

這只是拼圖的一部分。產品必須分解為微服務。每個服務都應該對自己的事情負責。例如,Netflix 中的推薦服務應該負責向使用者提供推薦。

並非所有東西都是 Netflix,也絕對不是所有東西都那麼容易分解成合適的規模並承擔合適的責任。這就是 DDD有界上下文可以提供幫助的地方,但一方面,實踐起來並不容易,有時甚至沒有足夠的時間/開放性來處理這些事情。

 

配套文化

無論如何,在我看來,微服務的第二個難點是組織/專案文化。如果產品(部門)不在乎底層系統架構怎麼辦?我是說他們會嗎?

一個例子:如果你有一個包含大量微服務的複雜架構怎麼辦。產品負責人進來對團隊說,讓我們開發整個功能。在團隊分析了功能請求後,結果發現它將涉及 10-15 個微服務,因為它與許多其他現有功能相關聯。在這種情況下你會怎麼做?

您嘗試將它分解成更小的部分,但它聞起來很可疑,因為該功能沒有任何意義,並且會增加很多開銷以逐個服務地釋出它。您當然不能因為我們使用微服務就對產品負責人說這需要 3-4 倍的時間,對嗎?

那次談話會是什麼樣子?

  • 產品負責人:大家好,我想出了這個非常棒的功能。我們的競爭對手已經在做,所以我們必須儘快做。有可能在 2 周內完成嗎?
  • 團隊:嗯,從最初的樣子來看,是的,我們可以做到。而且該功能看起來也是帶來更多客戶的好主意。我們會重新組合並討論它。
  • 團隊:好的,那兩週有點小問題。由於我們在做微服務是為了更快,我們需要更多的時間來實現這件事,因為我們必須接觸 15 個服務,所以我們需要大約 6 周的時間來進行初始實現。
  • 產品負責人:初始實施?
  • 團隊:是的。有 15 項服務對通訊至關重要,因此初始實現將不包括錯誤處理、彈性通訊模式、用於除錯目的的跟蹤和其他整潔的事情。為此,我們需要額外的 4 周。

(banq注:該部分指出複雜性Complicated系統中存在溝通高的成本,這些可以通過團隊拓撲 等人員管理來緩解,但是,需要看到進步:微服務已經將系統的複雜性轉嫁到管理複雜性,意識到這點是一種進步,你需要有 康威定理 的認知前提。) 

更好的故障隔離

這個自然是真的。如果一項服務出現故障,那麼只有該服務會出現故障,對嗎?

雖然這有點真實,但它不是非黑即白。

。。。

讓我們再舉一個例子。使用者嘗試使用登入服務登入系統。資料加密服務仍然失敗,登入服務正在呼叫分析服務以獲取有關在一分鐘時間內嘗試登入的使用者數量和其他一些虛構指標的一些指標。不過,分析服務正在與資料加密服務對話,因為這些資料也需要加密。

現在,編寫分析服務的團隊匆匆忙忙,沒有時間實施正確的錯誤處理,因此資料加密服務的問題會影響到登入服務。顯然,登入服務已經在幾個月前完成,並且該服務還沒有準備好處理來自分析服務的潛在錯誤,因此即使非關鍵分析服務失敗,使用者登入也會被簡單地拒絕。

我知道你的想法。是的,實施登入服務的團隊沒有為這種情況做好準備是不負責任的,但如果他們認為分析服務會優雅地處理這個問題呢?這寫在分析服務的 API 合同中,但它不能那樣工作。

那麼當你在一個單體應用中會發生什麼?服務崩潰在該上下文中並沒有真正的意義,但假設由於某種原因連線到資料加密的資料庫表不可訪問。

在這種情況下,錯誤處理將很簡單,因為您唯一需要準備的就是異常。儘管在過分讚揚單體應用之前,也有不利的一面,如果單體應用倒閉,什麼都不起作用。所以這是一個平衡問題,但問問你自己。實現 try-catch 塊或處理同步 HTTP 呼叫/非同步訊息傳遞錯誤是否更容易?

我記得用 80 多個微服務標準化錯誤處理是一項艱鉅的任務,一個團隊花了幾個月的時間來完成它。這甚至並不意味著在任何地方引入錯誤處理,而只是將現有錯誤重寫到我們使用的自定義庫中,這樣我們就可以減少未來錯誤處理場景所需的繁瑣工作。

(banq注:微服務的DevOps運維模式來幫助緩解故障隔離和修復,這也是一種進一步,雖然不如單體+單個團隊來得更具有效率,但至少是去中心化的非單點故障修復,單點故障修復需要燒香拜佛。。。。。)

  

我還沒說完。我們第 2 章見。

 

相關文章