使用微服務失敗的11個原因 - Shekhar Gulati

banq發表於2020-03-04

在過去的幾年中,我已經對處於數字化轉型過程中的多個產品團隊進行了架構審查。大多數團隊都在按照微服務架構構建產品。他們有使用基於微服務的體系結構的所有正確意圖-更快的開發,更好的可伸縮性,更小的獨立團隊,獨立的部署,使用正確的技術來完成工作,等等。但是,我經常發現團隊在微服務方面苦苦掙扎。他們未能充分利用微服務的優勢。在這篇文章中,我將分享我認為團隊在微服務方面苦苦掙扎的原因。
微服務架構風格是一種將單個整體應用程式開發為一組小服務的方法,每個小服務都在自己的程式中執行並與輕量級機制(通常是HTTP資源API)進行通訊。這些服務圍繞業務功能構建,並且可以由全自動部署機制獨立部署。這些服務的集中管理幾乎沒有,它可以用不同的程式語言編寫並使用不同的資料儲存技術。

原因1:管理層低估了開發微服務的複雜性
我曾與多個對微服務非常看好的客戶一起工作。對他們來說,微服務是解決所有問題的靈丹妙藥。在討論中,我發現大多數團隊及其管理層低估了微服務開發的複雜性。
要開發微服務,開發人員需要高效的本地環境設定。
隨著系統中服務的增加,越來越難在一臺計算機上執行應用程式的子集。當您使用消耗相對較多記憶體的語言(例如Java)構建應用程式時,尤其會發生這種情況。
以下是與本地開發設定有關的要點。

  1. 本地開發的第一個重要方面是好的開發機器。我發現大多陣列織都很難使用所有最新和最先進的技術,但是他們不想替換效能低下的Windows開發機器。開發人員受其開發機器的限制。我已經看到開發人員使用VDI映像或配置較差的機器來構建基於微服務的系統。這降低了他們的生產力,他們無法完全應用自己。使用效能不佳的開發機器的副作用是開發人員無法獲得快速反饋。如果您知道必須等待幾分鐘才能執行整合測試套件,那麼您寧願不編寫更多內容也不會增加工作量。不良的開發機器會導致不良的開發實踐。
  2. 一旦有了合理的機器開發人員就可以工作。接下來的事情是確保所有服務都使用構建工具。應該能夠在無需太多配置的情況下在一臺新計算機上構建整個應用程式。以我在微服務方面的經驗,使用一個root構建指令碼來構建整個應用程式是有幫助的。
  3. 下一個重點是使開發人員能夠輕鬆地在系統上執行應用程式的某些部分。您應該使用多個docker-compose檔案在配置了所有埠和卷的情況下啟動不同的服務。
  4. 接下來,如果您使用的是Kubernetes之類的容器編排工具,那麼您應該投資於Telepresence之類的工具,以便在Kubernetes叢集中輕鬆除錯應用程式。

如果組織不瞭解微服務的開發複雜性,那麼團隊速度將隨著時間下降。

原因2:沒有將庫和工具更新到最新版本的過程
新平臺已經成為歷史。團隊無法確保依賴關係保持最新狀態,或者資料庫等工具是否為最新版本。因此,兩年前開始的現代化工作如今已經有數月的技術債務。
幾年前,許多團隊開始將Spring Cloud Netflix OSS專案用於微服務。他們使用的是Kubernetes之類的容器編排工具,但是由於它們是從Netflix OSS開始的,因此並未使用Kubernetes提供的所有功能。當Kubernetes內建服務發現時,他們仍將Eureka用作服務發現。此外,藉助Istio之類的服務網格,您可以擺脫Netflix OSS提供的大多數功能。這有助於降低複雜性,並將很多交叉問題轉移到平臺上。
要記住的另一點是,要使所有服務的依賴項版本保持同步。我最近在幫助一個使用Spring Boot構建微服務的客戶。在過去的兩年中,他們已經構建了20多個Spring Boot服務。在他們的環境中,他們使用的Spring Boot版本範圍從1.5到2.1。這意味著當有人設定他們的機器時,他們必須下載多個版本的Spring Boot。此外,他們缺少自1.5以來在Spring Boot中所做的許多改進。
我們的建議是組織應在積壓的訂單中為這些升級建立技術債務專案。這些技術債務專案應在架構委員會會議上進行討論並定期解決。在我的上一個專案中,我們每三個月設定一個星期的衝刺,以將所有依賴項更新到最新版本。
此外,團隊應該花時間在升級工具上,例如資料庫,訊息佇列和快取,以升級到最新版本。

原因3:將共享服務用於本地開發
由於本地開發環境不佳,大多數團隊開始依靠共享環境提供關鍵服務。開發人員機器中的第一件共享就是資料庫。大多數年輕的開發人員都沒有意識到基於共享資料庫的開發是邪惡的。以下是我在共享資料庫中遇到的主要問題:

  1. 團隊成員必須建立工作的社會契約,以避免最後的寫入勝出問題。開發人員可以清除另一位開發人員為其工作編寫的資料。這種工作方式既痛苦又容易失敗。遲早這會咬傷團隊。
  2. 開發人員擔心實驗會影響他們的其他團隊成員。我們都知道,更好的學習方法是實驗和快速反饋。有了共享資料庫,就可以進行實驗了。我們需要進行實驗以提出資料庫架構並執行效能調整之類的任務。
  3. 另一個副作用是很難單獨測試更改。您的整合測試變得不穩定。從而進一步降低了顯影速度。
  4. 共享資料庫必須像寵物一樣對待,因為您不希望共享資料庫具有不一致且不可預測的狀態。您可能有一個開發人員想要在表為空但其他人需要表具有記錄的情況下測試邊緣情況。
  5. 僅共享資料庫具有系統正常工作所需的所有資料。隨著時間的流逝,團隊成員失去了更改的可追溯性,因此沒人知道他們如何在計算機上覆制相同的設定。唯一的方法是獲取完整的資料庫轉儲並使用它。
  6. 未連線到網路時很難工作。通勤時間長或乘飛機時,通常會發生這種情況。

資料庫只是共享服務的一個示例,但它也可以是訊息傳遞佇列,像Redis這樣的集中式快取或服務可以改變的任何其他服務。
解決此問題的最佳方法是使開發人員可以輕鬆地在其本地計算機上執行資料庫(作為docker容器),並投資建立SQL指令碼來設定架構和初始主資料。這些SQL指令碼應保留在版本控制中,並像其他任何程式碼一樣進行維護。

原因4:版本控制託管平臺缺乏可見性
一個版本控制系統中具有1000多個儲存庫。他們正在使用Gitlab版本控制平臺。他們有5個產品,每個產品都由多個微服務組成。我問他們的第一個問題是幫助我們瞭解哪些服務及其各自的程式碼儲存庫是產品A的一部分。他們的首席架構師不得不花一天的時間弄清楚構成產品A的所有儲存庫。不知道她是否涵蓋了所有服務。
解決此問題的最佳方法是從一開始就以某種方式對微服務進行分組,以使您始終對產品生態系統具有可見性。Gitlab提供了一種建立組,然後在其中建立專案儲存庫的方法。Github沒有組功能,因此您可以使用主題或命名約定來實現它。
我個人更喜歡mono repos,因為我發現它們真的很方便。我遇到的大多數開發人員都將其視為反模式。我同意Dan Lua的帖子,他在文章中提到了mono repo的以下好處
*簡化的組織
*簡化的依賴關係
*工具
*跨專案變更

原因5:沒有明確的服務定義
大多數團隊都不知道應該將什麼視為服務。關於實際上構成單個微服務的東西有很多困惑和困惑。
讓我們舉一個例子,您的應用程式具有類似於外掛的架構,您將在其中與多個第三方服務整合。每個整合都應該是微服務嗎?我已經看到多個團隊正在為每個整合建立一個微服務。隨著整合數量的增加,這很快變得難以管理。這些服務通常太小,以至於它們作為單獨的程式執行會增加更多的開銷。
我認為擁有少量大型服務總比擁有太多小型服務好。我將首先建立一個對業務組織中整個部門建模的服務。這也符合DDD。我將一個域分為子域和有界上下文。有界上下文表示公司內部的部門,例如財務和市場營銷。您可能認為這可能會導致大型微服務,這是正確的。但是,以我的經驗來看,將單體重構為微服務總是比反之容易。隨著您獲得更多的知識,您可以轉向代表較小關注點的細粒度微服務。您可以應用“單一責任原則”來了解您的微服務是否變得太大而做太多的事情。然後,您可以將其分解為較小的獨立服務。任何服務都不應直接與另一個服務的資料庫對話。他們只能透過已釋出的服務合同進行溝通。
我也遵循後端文件中提到的建議。該建議可以幫助限制服務之間的服務通訊,這是基於微服務的系統效能低下的首要原因。
如果兩條資訊相互依賴,則它們應屬於一臺伺服器。換句話說,服務的自然邊界應該是其資料的自然邊界。

原因6:沒有明確的程式碼重用策略
我正在與一個客戶一起工作,該客戶已在其所有基於Java的微服務中複製了四個與特定問題相關的Java檔案。因此,如果在該程式碼中發現錯誤,則需要將其應用到所有地方。我們都知道,在時間壓力下,我們會錯過將更改應用於一項或多項服務的機會。這將浪費更多時間並增加挫敗感。
不是說開發團隊不知道正確的事情。但是,組織的結構化方式人們總是預設使用簡單且容易出錯的做事方式。
正確的方法是使用工件管理器(如Bintray或Nexus)並在其中釋出依賴項。然後,每個微服務都應依賴該庫。您需要構建工具,以便在釋出新版本的庫時,應更新並重新部署所有微服務。
使用微服務並不意味著您不應使用迄今為止對我們有用的最佳實踐。您需要在工具上進行投資,以使其易於升級微服務。
在沒有適當工具和自動化的情況下使用微服務是災難的根源。

原因7:多種語言程式設計
我碰到了使用多種程式語言,多個資料庫,多個快取的團隊,這是工作的最佳工具。所有這些都在專案的初始階段起作用,但是當您的產品投入生產時,這些選擇就開始顯示出它們的真實色彩。諸如我們在構建Java Spring Boot應用程式之類的原因,但我們意識到Java消耗了更多的記憶體,並且效能很差,因此我們決定切換到Node.js。在上一個任務中,我向團隊解釋說他們的推理能力很弱。

  1. Node.js的效能優於Java。如果您有基於IO的工作負載,則Node.js通常會表現更好。Java 在任何計算密集型工作負載上均勝過 node.js。透過使用響應式範例,Java for IO工作負載可以具有更好的效能。Spring Boot Reactor在IO工作負載方面的效能與Node.js 相當
  2. Node.js消耗的記憶體少於Java。這部分是正確的,因為Node.js應用程式通常使用的記憶體少於Java。Java Spring Boot應用程式並不像大多數人想象的那樣糟糕。我在其中一個Spring Boot Java Microservice上進行了負載測試,並且記憶體消耗仍然少於1 GB。您可以透過OpenJ9 JVM,限制對類路徑的依賴性以及透過調整預設JVM引數來最佳化Java記憶體利用率。另外,Java中的Spring Boot也有其他替代方法,例如Micronaut和Quarkus,它們消耗的記憶體等於Node.js。
  3. Node.js比Java更高效。這取決於開發人員編寫程式碼。帶有靜態型別和靜態分析工具的Java可以幫助在開發生命週期的早期發現問題。

大多數時候,這全都取決於上下文。如果您的開發人員不成熟,則無論使用哪種程式語言,您都將開發出不良的產品。
我建議組織釋出團隊可以使用的語言列表。我認為2–3是個好數字。另外,列出為什麼應使用一種語言代替另一種語言的原因。
選擇語言之前,應考慮以下多種原因:
  1. 輕鬆找到成熟的企業軟體開發人員有多容易?
  2. 重新培訓開發人員使用新技術有多容易?我們發現Java開發人員可以相對輕鬆地學習Golang。
  3. 最初團隊之外的開發人員可以多麼容易地貢獻,傳輸和維護他人編寫的程式碼?
  4. 就工具和庫而言,生態系統的成熟程度如何?

這不僅限於程式語言。這也適用於資料庫。如果您的系統中已經有MongoDB,那麼為什麼要在生態系統中使用ArangoDB?它們都主要是文件資料庫。
始終考慮使用多種技術的維護和操作方面。

原因8:人們依賴性
這並非特定於微服務,但在微服務生態系統中變得更加普遍。原因是大多數團隊專注於他們的特定服務,因此他們不瞭解完整的生態系統。在與不同客戶的合作中,我發現只有一小部分了解整體情況的建築師。但是,這些架構師的問題在於他們在日常活動中不活躍,因此對開發的影響有限。
我認為最好的辦法是確保所有團隊在架構小組中只有一個代表部分,以便他們可以使團隊與整體架構團隊的路線圖和目標保持一致。要成為一個成熟的組織,您需要投資建立輕量級的治理。

原因9:缺少文件
過去幾年中與我們互動的大多陣列織都在文件方面苦苦掙扎。大多數開發人員和架構師要麼不編寫文件,要麼他們編寫的文件沒有用。即使他們想寫,他們也不知道應該如何記錄其體系結構。
至少我們應該記錄以下內容:

  1. 設計檔案
  2. C4模型中的上下文和容器圖
  3. 架構決策記錄的形式跟蹤關鍵架構決策
  4. 開發人員入門指南

我建議所有文件都保留在版本控制系統中。

原因10:功能超過平臺成熟度
我在其他方面簡要地談到了這個原因,但是我認為值得一提的是頂級原因。微服務比傳統的單片單體應用程式更為複雜,因為您正在構建具有許多活動部件的分散式系統。大多數開發人員尚未理解系統的不同故障模式。大多數微服務在構建時都考慮了一條愉快的路。因此,如果您的管理層只想早於專注於功能,那麼您將失敗。在弱平臺上構建的功能無法帶來價值。
組織需要進入平臺心態。平臺心態不僅意味著使用容器和Kubernetes。它們是解決方案的一部分,但本身並不是完整的解決方案。您需要考慮分散式跟蹤,可觀察性,混亂測試,函式呼叫與網路呼叫,服務間通訊的安全服務,可除錯性等。這需要大量的精力和投資來建立合適的平臺和工具團隊。
如果您是一家資源有限的初創公司,我的建議是重新考慮您的微服務策略。請了解您正在進入什麼。

原因11:缺乏自動化測試
大多數團隊都知道自動化測試對產品的整體質量有多重要,但他們仍然沒有做到。微服務架構為在何處以及如何進行測試提供了更多選擇。如果您不進行全面的自動化測試,那麼您將嚴重失敗。

原因12:您甚至無法構建結構良好的單體架構。

相關文章