微服務失敗的11個緣由

在過去的幾年裏,我對進行數字化轉型的多家產品團隊進行了架構審查。大多數團隊都是遵循微服務架構來構建產品。他們徹底有理由使用基於微服務的架構:更快的開發、更好的可擴展性、更小的獨立團隊、獨立的部署、使用正確的技術來完成工做,等等。可是,我常常發現,團隊在微服務方面舉步維艱。他們未能充分利用微服務的優點。在本文中,我將分享個人觀點,闡述團隊在微服務方面爲什麼舉步維艱的緣由。react


對於剛接觸微服務的新手來講,我推薦閱讀 Martin Fowler 關於微服務的文章。我很喜歡這篇文章中提到的微服務架構定義。docker


微服務架構風格就是一種將單個應用程序開發成一套小型服務的方法,每一個應用程序都在本身的進程中運行,並與輕量級機制(一般是 HTTP 資源 API)進行通訊。這些服務是圍繞業務功能而構建的,而且能夠由徹底自動化的部署機制來獨立部署。這些服務只有最低限度的集中管理,能夠用不一樣的編程語言編寫,並使用不一樣的數據存儲技術。數據庫


01編程

管理層低估了開發微服務的複雜性後端


我曾與許多很是看好微服務的客戶一塊兒合做過。對他們來講,微服務就是解決他們全部問題的靈丹妙藥。在與我討論中,我發現大多數團隊及其管理層低估了微服務開發的複雜性。數組


要開發微服務,開發人員須要一個高效的本地環境設置。緩存


隨着系統中的服務數量開始增長,在一臺機器上運行應用程序的子集變得愈來愈困難。特別是當你使用消耗較多內存的語言(如 Java)構建應用程序時,更是如此。安全


下面是與本地開發設置相關的要點。服務器


(1)本地開發的第一個重要方面是要有一個好的開發機器。我發現,大多數組織想要使用全部最新的、最早進的技術,但卻不想替換掉糟糕的 Windows 開發機器。開發人員受限於他們的開發機器。我曾見過開發人員使用 VDI 映像或配置交叉的機器來構建基於微服務的系統。這下降了他們的工做效率,使他們沒法徹底投入工做。使用糟糕的開發機器帶來的反作用就是,開發人員沒法獲得快速的反饋。若是你知道必須等待數分鐘才能運行集成測試套件,那麼你就不會編寫更多的集成測試套件,省得給你帶來痛苦。糟糕的開發機器將會致使糟糕的開發實踐。微信


(2)一旦你爲開發人員配備了合適的開發機器,那麼下一步就是確保全部服務都使用構建工具。你應該可以在一臺新機器上構建整個應用程序,而不須要進行太多配置。根據我在微服務方面的經驗,使用可以構建整個應用程序的根構建腳本也會有所幫助。


(3)下一個要點就是要讓開發人員可以輕鬆地在他們的系統上運行應用程序的各個部分。在配置了全部端口和卷的狀況下,你應該使用多個 docker-compose 文件來提供不一樣的服務。


(4)接下來,若是你使用的是相似於 Kubernetes 的容器編排工具,那麼你應該投資相似於 Telepresence 這樣的工具,以便輕鬆調試 Kubernetes 集羣中的應用程序。


若是組織對微服務開發的複雜性缺少理解,那麼團隊的速度將會隨着時間的推移而降低。


02

未將庫和工具更新到最新版本


在我看來,我發現一個新的平臺已經成爲一種遺產。團隊沒有確保依賴項是最新的版本,或者將像數據庫這樣的工具升級到最新版本。所以,兩年前開始的現代化改造,現在已經揹負了長達數月的技術債務。


幾年前,許多團隊開始將 Spring Cloud Netflix OSS 項目用於微服務。他們使用像 Kubernetes 這樣的容器編排工具,可是由於他們是從 Netflix OSS 開始的,因此他們沒有使用 Kubernetes 提供的全部功能。當 Kubernetes 內置了服務發現時,他們仍然使用 Eureka 做爲服務發現。此外,經過相似 Lstio 這樣的服務網格,你就能夠擺脫 Netflix OSS 提供的大部分服務。這有助於下降複雜性,並將許多「橫向關注點」(cross cutting concerns)轉移到平臺上。


須要記住的另外一點是,要使全部服務的依賴項版本保持同步。我最近在幫助一個客戶,他使用 Spring Boot 來構建微服務。在過去兩年中,他們已經構建了 20 多個 Spring Boot 服務。在他們的環境中,他們使用的 Spring Boot 版本從 1.5 到 2.1 不等。這意味着當有人設置他們的機器時,他們必須下載多個版本的 Spring Boot。此外,他們還缺少自版本 1.5 以來在 Spring Boot 中所作的許多改進。


咱們的建議是,組織應該在其積壓中爲這些升級建立技術債務項。這些技術債務項應做爲架構委員會會議的一部分加以討論,並按期予以解決。在個人上一個項目中,咱們每三個月設置爲期一週的 sprint,將全部依賴項更新到最新版本。


03

利用共享服務促進本地開發


因爲本地開發情況不佳,大多數團隊開始依賴於共享環境來得到關鍵服務。開發人員機器中的第一件事就是數據庫。大多數年輕的開發人員並無意識到基於共享數據庫的開發是「邪惡的」。下面,是我在共享數據庫中看到的主要問題:


(1)團隊成員必須創建一個工做的社會契約,以免最後寫入者勝出(Last write wins,LWW)問題。一個開發人員能夠刪除其餘開發人員爲他們的工做編寫的數據。這種工做方式既痛苦又容易失敗,早晚會影響整個團隊。


(2)開發人員懼怕實驗,由於他們的工做會影響其餘團隊成員。咱們都知道,更好的學習方法是實驗和快速反饋。有了共享數據庫,就能夠進行實驗了。咱們須要進行實驗,以提出數據庫模式,並執行任務,如性能調優之類。


(3)另外一個反作用就是,很難單獨測試更改。你的集成測試將變得不可靠,從而進一步下降了開發速度。


(4)共享數據庫必須像寵物同樣對待,由於你不但願它出現不一致和不可預測的狀態。你可能會遇到這樣一種場景,開發人員但願在表是空的時候測試邊緣狀況,但其餘開發人員須要一個表來記錄。


(5)只有共享數據庫擁有系統工做所需的全部數據。隨着時間的推移,團隊成員失去了更改的可追溯性,所以沒有人知道,他們該如何在他們的機器上覆制相同的設置。惟一的方法是獲取完整的數據庫轉儲並使用它。


(6)若是未鏈接到網絡,就很難開展工做。這種狀況一般發生在你通勤時間過長或乘飛機的時候。


數據庫只是共享服務的一個示例,但它也能夠是消息隊列、集中緩存(如 Redis)或任何其餘服務能夠發生改變的服務。


解決這一問題的最好方法是,讓開發人員能夠輕鬆地在他們的機器上運行數據庫(做爲 Docker 容器),並投資建立 SQL 腳原本設置模式和初始主數據。這些 SQL 腳本應該保存在版本控制中,並像維護任何其餘代碼同樣進行維護。


04

版本控制託管平臺缺少可見性


我曾與一個客戶進行合做,當時,他們的版本控制系統中有 1000 多個倉庫。他們使用的是 Gitlab 版本控制平臺。他們有 5 個產品,每一個產品都由多個微服務組成。我問他們的第一個問題是幫助咱們瞭解哪些服務及其各自代碼庫是產品 A 的一部分。他們的首席架構師不得不花一天時間弄清楚全部產品 A 的倉庫。一天過去了,她仍是不能肯定是否弄清楚了全部的服務。


解決這個問題的最好方法是,從一開始就以某種方式對你的微服務進行分組,這樣,你就能夠隨時瞭解產品的生態系統。Gitlab 提供了一種方法來建立一個組,而後在其中建立項目倉庫。Github 並無組功能,所以你可使用主題或命名約定來實現它。


我我的更喜歡 mono repos,由於我發現他們真的很方便。我遇到的大多數開發人員都認爲它是一種反模式。我贊成 Dan Lua 的觀點,他提到了 mono repo 的如下好處:

· 簡化的組織

· 簡化的依賴關係

· 工具

· 跨項目變動


05

服務沒有明確的定義


大多數團隊並不知道什麼應該被視爲服務。關於到底是什麼構成一個單一的微服務,人們對此存在不少混淆的認識和困惑的概念。讓咱們舉一個例子,假設你的應用程序具備相似插件的架構,在這個架構中,你集成了多個第三方服務。每一個集成應該是一個微服務嗎?我看到不少團隊,都在爲每一個集成建立一個微服務。隨着集成數量的增長,這種狀況很快就會失控,以致於沒法管理。這些服務一般過小,以致於將它們做爲單獨的進程運行,會增長更多的開銷。


我認爲,哪怕只擁有少許的大型服務,總比提供太多的小型服務要好得多。我將從建立一個服務開始,該服務對業務組織中的整個部門進行建模。這也符合 DDD(領域驅動設計, Domain Driven Design)。我將一個域分爲子域和有界上下文。有界上下文表示公司內部的一個部門,如財務部門和營銷部門。你可能認爲,這會致使大型服務的出現,你是對的。可是,以個人經驗來看,將總體重構爲微服務總之比反之更容易。


隨着你的知識經驗愈來愈多,你能夠轉向表明更小問題的細粒度微服務。你能夠應用單一責任原則(Single Responsibility Principle)來了解你的微服務是否變得過大,作的事情是否過多。而後,你能夠將它分解成更小的獨立服務。任何服務都不該該直接與其餘服務的數據庫通訊。他們應該只經過已發佈的合同進行溝通。你能夠在 Microservices.io 網站上閱讀更多關於按子域模式分解 的內容。


我也遵循了 Backendlore 文檔中提到的建議。這個建議能夠幫助將服務限制在服務通訊上,而服務通訊是微服務系統性能低下的首要緣由。若是兩條信息相互依賴,那麼它們應該屬於同一個服務器。換句話說,服務的天然邊界應該是其數據的天然邊界。


06

代碼重用策略不明確


我曾經和一個客戶合做,該客戶在他們全部基於 Java 的微服務複製了四個與特定問題相關的 Java 文件。所以,若是在該代碼中發現 bug 的話,就須要將其修復應用到全部地方。咱們都知道,在時間緊迫的狀況下,咱們會錯過將更改應用於一個或多個服務。這樣會浪費更多的時間,增長挫敗感。


這並不是說開發團隊不懂正確的事情。可是,按照組織結構,人們老是默認採用簡單且容易出錯的作事方式。


正確的方法是,使用像 Bintray 或 Nexus 這樣的工件管理器,並在那裏發佈依賴項。而後,每一個微服務都應該依賴於這個庫。你須要構建工具,以便在發佈新版本的庫時,全部的微服務都應該進行更新和從新部署。


使用微服務並不意味着你不該該使用迄今爲止對咱們有用的最佳實踐。你須要對工具進行投資,使微服務的升級變得更容易,這樣人們就沒必要這樣作了。


在沒有適合的工具和自動化的狀況下,使用微服務會致使災難。


07

多語言編程設計


我發現團隊使用多種編程語言、多種數據庫、多種緩存,並以最佳工具的名義進行工做。全部這些都在項目的初始階段起做用,但隨着你的產品投入生產,這些選擇開始顯露出它們的本色。緣由就像咱們在構建 JavaSpringBoot 應用程序,可是咱們意識到 Java 佔用了更多的內存,且性能也不好,因此咱們決定改用 Node.js。在我上一次任務中,我向團隊解釋說他們的推理能力很弱。


Note.js 比 Java 性能更好。 若是你的工做負載是基於 I/O 的,Node.js 一般會表現的更好。但在任何計算密集型的工做負載上,Java 都賽過 Node.js。經過響應式範式(reactive paradigm),你可使用 Java 爲 I/O 工做負載提供更好的性能。在 I/O 工做負載方面,Spring Boot Reactor 的性能至關於 Node.js。


Node.js 比 Java 消耗更少的內存。 這在必定程度上是正確的說法。Java Spring Boot 應用程序並不像大多數想象的那麼糟糕。我在一個 Spring Boot Java 微服務上運行了負載測試,內存消耗仍然沒超過 1 GB。你能夠經過 OpenJ9 JVN,限制對類路徑的依賴,並經過調優默認的 JVM 參數來優化 Java 內存利用率。此外,在 Java 中還有 Spring Boot 的新替代品,如 Micronaut 和 Quarkus,它們消耗的內存至關於 Node.js。


Node.js 比 Java 效率更高。 這取決於編寫代碼的開發人員。使用靜態類型和靜態分析工具的 Java 能夠幫助在開發生命週期的早期發現問題。


大多數狀況下,這徹底取決於上下文。若是你的開發人員還不夠成熟的話,那麼不管你使用什麼編程語言,你開發的都將是糟糕的產品。


我建議一家組織要發佈一個團隊可使用的語言列表。我認爲 2~3 就是個很不錯的數字。另外,要列出一種語言比另外一種語言更適合的理由。


在選擇一門語言以前,你應該考慮如下一些問題:


(1)找到成熟的企業軟件開發人員有多容易?

(2)從新培訓開發人員掌握新技術有多容易?咱們發現 Java 開發人員能夠相對容易地學習 Golang。

(3)初始團隊以外的開發人員貢獻、轉移和維護其餘人編寫的代碼有多容易?

(4)就工具和庫的方面而言,生態系統有多成熟?


這不只僅侷限於編程語言,也適用於數據庫領域。若是你的系統中已經有了 MongoDB,那麼你爲何要在生態系統中使用 ArangoDB 呢?它們都主要是文檔數據庫。


要始終考慮使用多種技術的維護和操做方面。


08

人員的依賴性


這並不是微服務特有的現象,但在微服務生態系統中卻變得更加廣泛。緣由是,大多數團隊專一於他們的特定服務,所以他們並不瞭解完整的生態系統。在我與不一樣客戶的工做中,我發現,只有一羣架構師瞭解總體狀況。可是,這些架構師的問題在於,他們並不積極參與平常活動,所以他們對開發的影響力是有限的。


我認爲最好的辦法是,確保全部團隊都有一個架構團隊的表明,這樣他們就可使他們的團隊與整個架構團隊的路線圖和目標保持一致了。要成爲一個成熟的組織,你須要投資於創建一個輕量級的治理。


09

文檔的缺少


在過去幾年裏,咱們接觸過的大多數組織都在文檔方面遇到了困難。大多數開發人員和架構師要麼不去編寫文檔,要麼編寫的文檔毫無用處。即便他們想寫,他們也不知道應該如何記錄他們的架構。


咱們至少應該記錄如下內容:


· 設計文檔。

· C4 模型中的上下文和容器圖。

· 以架構決策記錄的形式跟蹤關鍵架構決策。

· 開發人員入門指南。


我建議在版本控制系統中維護全部的文檔。


10

功能超過平臺成熟度


我已經在其餘觀點中簡要地提到了這個緣由,但我認爲,它值得做爲一個頂級緣由來說起。微服務要比傳統的單體式應用(monolithic application)更爲複雜,由於你正在構建一個包含許多活動部件的分佈式系統。大多數開發人員還不瞭解系統的不一樣故障模式。大多數微服務在構建時都考慮了使人快樂的路徑。所以,若是你的管理層只想僅僅關注功能,那麼你註定會失敗。由於在薄弱平臺上構建的功能是沒法提供價值的。


組織須要有平臺思惟。平臺思惟可不只僅意味着使用容器和 Kubernetes。它們是解決方案的一部分,但自己並不是完整的解決方案。你還須要考慮分佈式跟蹤、可觀察性、混沌測試、函數調用與網絡調用、服務間通訊的安全服務、可調試性等等。這須要在構建正確的平臺和工具團隊方面付出認真的努力和投資。


若是你是一家資源有限的初創公司,個人建議是,你要從新考慮微服務戰略。瞭解你所面臨的問題是什麼。


11

缺少自動化測試


大多數團隊都知道自動化測試對產品的總體質量有多重要,可是他們仍然沒有作到。微服務架構爲測試地點和測試方式提供了更多的選擇。若是不進行完全的自動化測試,那麼你將會失敗得很慘。


關於這一點,我不會再贅述,由於網上不少人都寫過這方面的內容了。下圖是我從微服務測試的文章找到的,這篇文章來自 Martin Fowler 的網站,討論了基於微服務的系統的測試金字塔。


(微服務測試金字塔)



- END -


往期回顧

單元測試的五個主要準則

實踐微服務六年,我得到了這些心得體會

業務百倍增加,得物如何在三個月完成交易平臺重構?


點一下在看再走吧


本文分享自微信公衆號 - 互聯網後端架構(fullstack888)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索