天天看點

微服務

微服務現在受到了大量的關注︰ 文章、 部落格、 社交媒體和學術會議上的讨論都能看到該詞彙的身影。微服務正迅速走向 Gartner Hype cycle 所指的快速發展期。同時,軟體社群的一些懷疑論者指出微服務并不是什麼新鮮玩意兒。這些唱反調的人說微服務和SOA概念并沒有什麼不同,舊瓶裝新酒而已,順勢炒炒新概念。然而,不管說是誇大也好,懷疑也罷,微服務架構模式應用在靈活開發和傳遞複雜的企業應用程式的時候還是有巨大優勢的 。

本部落格是設計、建構和部署微服務七篇部落格系列的開篇。通過該文章将學到一些微服務的方法,還有微服務架構與傳統單體式架構模式的比較。本系列将介紹微服務架構的各種元素,并揭示該架構模式的各種優點與缺點,以此來指導微服務是否适合您的項目,以及如何運用該模式。

讓我們首先看看為什麼你應該考慮使用微服務。

建構單體式應用

假設現在我們為了與Uber和Hailo競争來建構一個全新線上打車軟體:經過一系列的預備會議和需求收集,我們決定無論是人工撸還是用Rails,Spring Boot,Play,或Maven之類工具生成也好,最終要建立一個全新的應用!應用應該有如下圖六邊形一樣的子產品結構

微服務

Paste_Image.png

應用程式的核心是業務邏輯,它了定義服務、 領域對象和事件子產品。圍繞核心是接口與外部世界的擴充卡。擴充卡的例子包括資料庫通路元件、 生産消息和消費消息的消息傳遞元件以及暴露API或實作使用者界面的 web 元件。

盡管在邏輯上子產品化,整個應用還是做為一個巨大的整體進行打包和部署的。而且子產品的結構往往與選擇的程式設計語言和架構緊密相關。 舉個栗子: 多數java程式打成war包部署到Tomcat 、Jetty等應用伺服器中;還有一部分被打成自包含的jar包(Spring Boot支援把jetty或者tomcat包進去,以main為入口啟動應用); 同樣的,Rails 和 Node.js 應用直接以目錄結構的方式部署.

這種風格開發的應用是非常常見的。我們的IDE和工具緻力于建構這樣的單一應用,是以我們開發起來也感覺到簡單;此類應用測試起來也很友善,你可以很簡單的啟動這個應用,采用selenium測試UI去完成端到端的測試;單體應用部署起來也不難,把包一拷貝扔伺服器裡就妥了, 如果要擴充應用,隻需要在負載均衡器後面跑多個相同的應用就可以了。工程的初期呢,這個方法工作的還是不錯的!

邁向單體的地獄

很不幸的是,這種方式還是有巨大的局限性的.。一個成功的應用總會随着時間逐漸成長并變得巨大起來。每個靈活Sprint周期,開發者會實作更多的功能,當然這就意味着又添加了很多行代碼。幾年之後你會發現,當年你寫的那個簡單的小小的應用居然變成一個巨大的單體怪物。舉一個比較極端的例子吧:最近我和一個開發者聊天,他正在寫一個工具來分析數百萬行代碼的巨大應用中的上千個jar的依賴關系。别的不說,寫出這麼巨大的怪物肯定花費了很多程式員幾年的時間。

一旦應用變成了巨大複雜的單體,那你的開發團隊将痛苦不堪!靈活開發和傳遞的一些願景在這個龐然大物面前都會受挫。最主要的問題就是這玩意兒現在太複雜了!複雜到很難有某個程式員能完全了解它!導緻結果就是,正确的修個bug、做個新功能變的費時又費力。更可恨的是,這是一個惡性循環的過程,應用正在逐漸的‘死去’ 。如果你的代碼庫别人都了解不了,那麼對代碼做點正确的改動是很難的,最終這個怪物更加的可怕更加的難以了解,糾結!

應用程式的規模大了也會拖慢程式的開發: 程式越大,啟動越慢!調查顯示一些程式員說他們啟動程式要12分鐘,我個人還聽過有的應用啟動要40分鐘!程式員開發過程需要周期性的重新開機應用,這樣就浪費了很多時間,效率自然也很低下,不能忍!

巨大、複雜的單體應用還是持續傳遞的巨大障礙。現在SaaS應用的宗旨是如果有改動能夠每天部署多次。單體應用的中某部分的改動需要需要重新部署整個應用,這樣的話持續傳遞是相當困難的。前面也講了,啟動一次就要那麼久 !另外,改動造成的影響也不是很好被了解,需要大量的手工測試去保證,這樣的話持續傳遞更是難上加難了!

單體應用在多個子產品對資源需求上有沖突時很難進行擴充。比如:一個新子產品可能實作了CPU密集型的圖檔處理邏輯,更适合部署到Amazon EC2 Compute Optimized instances。另一個子產品可能需要一個記憶體資料庫,更适合使用EC2 Memory-optimized instances。然而,這些子產品必須被一起部署的話,選擇硬體的時候就要好好折中一下喽。

單體應用的另一個問題是穩定性:因為所有的子產品都跑在同一程序之中,某子產品中的bug,比如記憶體洩漏是可能拖垮整個應用的!更重要的是,因為負載均衡後面的所有的應用是一樣的,bug還可能影響整個應用的可用性!

最後重要的一點:單體應用很難擁抱新的架構和程式設計語言。假設你有2百萬行用XYZ架構寫的代碼,如果用ABC架構重寫它将會耗費巨大的時間和金錢!哪怕大家都知道用新架構更适合一些(攤手)。這樣導緻的結果是,在嘗試轉換新架構新技術的時候存在巨大的障礙,我們不得不繼續在選型之初定好的技術架構上前行(哭臉)。

最後總結下吧,你從擁有一個業務清晰,幾個程式員都明白的小程式,逐漸的變成了一個可怕的單體應用。回首看這個應用是采用了過時的、效率低下的技術來寫的(畢竟技術在進步,抱着老東西悶頭陶醉不是什麼好事情)!别的不說,招聘都費勁呐!這個單體應用變的難以擴充、變得極不穩定,想靈活開發?想持續傳遞? 恐怕隻能呵呵了。

面對這些,你能做點什麼呢?

微服務 – 處理這些複雜問題!

很多機構,比如Amazon、eBay、Netflix,已經開始通過擁抱微服務架構去解決上述的問題了。她們不再去建構一個可怕的單體應用,而是拆分成多個更小的、互相連接配接的微服務!

服務通常實作了一系列互相獨立的功能或特性,比如訂單管理、客戶管理等等。每一個微服務都具有自身業務邏輯以及各種擴充卡構成的六角形架構模式,都是一個迷你的小應用。有的微服務會暴露API供其他微服務和客戶消費,其他微服務則可能實作Web UI。運作時,每一個執行個體通常是雲平台的一個VM或者一個Docker容器。

下面是對上述老架構的一個拆分:

微服務

應用的每個功能區域現在都以微服務的方式來實作了,此外整個應用被拆分成一系列更小的web應用(比如乘客管理、司機管理)。拆分之後更加友善的為特殊使用者、裝置或者特殊用例進行部署。

每一個後端服務都暴露REST API,絕大部分服務也會消費其他服務提供的API。比如,司機管理服務會使用通知服務告知空閑的司機潛在的行程(來生意了,接單!);還有UI 服務會調用其他服務後渲染web頁面。服務之間也會使用異步的,基于消息的通信模式,微服務程序間的通信将會在後面的文章詳述。

一些REST API 也會暴露給移動端裝置友善司機和乘客的使用。當然,這些應用不會直接通路後端各個微服務 ,而是通過一個協調者API網關來通路的。 API網關的職責有負載均衡、緩存、通路控制、API計費、監控···,這些事情使用NGINX就OK啦。後面的文章将會講述API網關的細節。

微服務

微服務架構模式很契合上圖的Y軸擴充方式,上圖X軸的擴充表示橫向擴充應用,一般就是負載均衡器後面部署幾個相同的應用。Z軸擴充表示資料分區,将請求路由到特定的伺服器上(比如資料庫分庫分表,根據主鍵通路到使用者記錄對應的資料庫)。

應用一般都會三個次元去擴充。Y軸正是按照我們第一節講到的,把單體應用拆分為微服務,運作時呢,為了某個服務達到高吞吐和高可用性,可以采用負載均衡,也就是X軸擴充了,特殊應用也會使用Z軸擴充來切分服務。下面的圖展示了行程管理采用Docker鏡像部署到Amazon EC2的情況:

微服務

運作時,行程管理由多個服務執行個體組成。每個應用執行個體實際是一個Docker容器。為了達到高可用,容器會跑在多個雲平台VM上,服務執行個體之前是NGINX這樣的負載均衡器來分發請求,負載均衡器也會關注比如緩存、權限通路、服務計費、監控等事宜。

微服務架構模式極大的影響應用和資料庫之間的關系。每個微服務都會有自己的資料庫Schema,而不是和其他服務共享一套資料庫Shcema。另一個角度看,這種方式對于以往企業級範圍的資料模型來講是很奇怪的,另外,微服務這麼搞會造成資料備援。然而,如果你想從微服務架構獲益,那麼每個微服務一個資料Schema是很有必要的,因為微服務提倡的就是松耦合啊,下面的圖展示了微服務架構下應用的資料架構:

微服務

每個微服務都有自己的資料庫,而且每個服務還能選擇适合自己需求的資料庫,這就是所謂的多态型持久化架構。比如,司機如果需要查找附近的潛在乘客,那就必須要使用支援高效地理位置查詢的資料庫。

表面上看微服務和SOA架構很相似,兩種架構都倡導由一系列的服務組成。我們可以把微服務架構模式當成是沒有商業web service規範(WS規範咯)以及ESB等打包套件限制的SOA。基于微服務架構的應用更傾向于簡單的、輕量級的REST協定而不是老舊的WS,當然,微服務也會避免去使用笨重的ESB套件而更喜歡去實作一些ESB部分功能的輕量級工具(不要捆綁,不要全家桶!),微服務架構模式也避免SOA中諸如規範化Schema的定義。

微服務的優勢

微服務架構模式有很多重要的優勢。首先,通過把可怕巨大的單體拆分為一系列的服務解決了單體複雜度問題, 拆分後整體功能數量并沒有變化,但是應用現在變成了多個友善管理的小應用,每個服務都有一個定義良好的服務邊界,通過RPC方式或消息驅動暴露API。微服務架構模式解決了單體應用代碼庫在實踐中極難子產品化的難題,拆分後的微服務能夠更快的部署,更易了解和維護!

第二,拆分後的服務可以被專注于某部分服務的團隊獨立開發。程式員可以基于API約定前提下自由選擇合适的技術,當然,很多機構為了避免技術選擇混亂也會限制技術的選項。是以,自由意味着程式員可以不再局限于項目初期選擇的技術了(撒花)!開始寫一個新的微服務的時候,開發者可以使用一些當下流行的技術,更重要的是,因為每個服務現在拆分的很小,使用現有技術重寫老的服務成為可能!

第三,微服務架構模式使得獨立部署成為可能,開發者不用再忙碌于協調其他子產品的變化再去部署(單體應用,改一個部分可能對其他部分影響,某個更改可能涉及多個子產品的協調),微服務的更改可以在測試後盡快的被部署。比如,UI組可以進行A/B測試并且快速疊代而不用等待整個應用部署。微服務架構模式使得持續部署成為可能!

最後,微服務架構模式使得每個服務可以獨立的擴充。我們可以針對某些有容量和可用性要求的微服務進行擴充,為需要的服務部署多個執行個體而不是複制多個單體去折中獲得某項提升!更重要的是,我們可以針對服務需求使用最合适的硬體資源。比如,我們可以把CPU密集型的圖檔處理服務部署到EC2 Compute Optimized instances,部署記憶體資料庫相關的服務到EC2 Memory-optimized instances。

微服務的劣勢

正如Fred 30年前的書中所說,“沒有銀彈!”。和其他技術一樣,微服務架構也有其劣勢 。劣勢之一就是它的名字(捂臉),微服務這個詞過分強調了服務的尺寸,實際上還真有幾個開發者号召大家寫10-100行代碼的‘微服務’(這應該不太可能吧)!然而微服務更想表達的是一種工具和途徑,微并不是最終的目标(為微服務而微服務,敲響警鐘),微服務是為了便利靈活開發和部署而去有效的拆分應用。

微服務另一個劣勢是因為從單體應用拆分為分布式系統帶來的複雜。開發者需要選擇或者實作基于消息或者RPC模式的程序間通訊機制,另外開發者也要寫額外的代碼去處理對于目的服務請求可能存在的請求緩慢或者請求不可用導緻的局部故障問題。分布式系統可不是什麼火箭科學,它可比單體應用中子產品間通過語言層面或者程序内調用複雜的多了!

微服務另一個挑戰就是拆分資料庫架構。一個邏輯事務更新多個業務記錄是很常見的事情,單體應用實作該事務是比較簡單的,畢竟大家使用一個資料庫。然而在微服務架構中,你必須要更新多個服務的多個資料庫。一般不會使用分布式事務,不僅僅是因為CAP理論,還因為一些流行的NoSQL資料庫和Message Queue系統壓根也不支援(攤手)。最後還得繞回最終一緻性方案,這個方案對開發者來講也是略有挑戰的(實際上我看這個系列的文章并且開始翻譯也是因為實際中遇到了這個問題,下面的文章将會提到)。

測試微服務架構的應用也是更加複雜的。比如,使用Spring Boot這種架構,開發者啟動一個單體應用去寫一些測試用例、測試其REST API是很平常事情。相反的是,相類似的測試在微服務中的話,你要啟動或者至少去mock其依賴的其他的服務才能完成。再次強調,微服務不是火箭科學,很重要的一點是大家不要低估了這樣做的複雜度!

微服務架構模式的另一巨大挑戰是實作跨服務的改動。比如,讓我們假設你要實作一個需要更改服務A、B、C的功能,而此時A依賴于B,B依賴于C。在傳統單體應用中,你可以很簡單的去更改對應的子產品,內建這些變動然後一起部署它們。然而在微服務架構模式下,你需要小心翼翼的計劃和協調每個服務的改動和釋出:你需要更新服務C,再更新服務B,然後還要更新服務A。幸運的是,絕大部分時候一個服務的變化僅會影響一個服務,多個服務間需要協調的情況是很少出現的。

部署一個基于微服務架構的應用是更加複雜的。一個傳統單體應用可以簡單的部署到負載均衡器後面,因為應用都是相同的,拷貝部署就可以了。每個應用配置好資料庫和message broker的位址(主機和端口)就好了。相對應的微服務一般是大量的服務組成的,比如,Hailo存在160個不同的服務,Netflix則有多達600多個不同服務,每個服務還有多個運作執行個體!将會有更多的變化的部分需要去配置、部署、擴充、監控。此外還需要實作一個服務發現機制讓其他服務找到它需要通信的服務的位址。傳統的基于Ticket和手動運維的方式是不可能處理好這個級别的複雜擴充的。是以,成功部署一個微服務需要開發者對部署方法有更多控制還要對服務執行高水準的自動化。

自動化的其中一種方式是使用現有的Paas平台(比如Cloud Foundry)。PaaS平台為開發者提供了便捷的方式去部署他們的微服務,避免了是他們糾纏于購買和配置IT資源的泥潭。同時,配置PaaS系統和網絡的專業人員可以確定遵守了最佳實踐和公司政策。另一種自動化方式就是開發自己的PaaS平台。一個典型的開端就是先使用叢集方案(比如Kubernetes)配合上Docker容器技術,後續的文章将會介紹基于軟體的應用傳遞方案,比如NGINX是如何在微服務層面友善的處理緩存、通路控制、API計費、監控等問題的。

總結

建構複雜的應用本身就是困難的事情。單體應用架構在針對簡單、輕量級的應用的時候是很好的。但是一旦你把那套方案運用在複雜的應用的時候你将會痛苦不堪。盡管微服務架構模式有諸多缺點和實作上的挑戰,但對于複雜的、演進的應用來講是一個更好的選擇。

在後續的文章中我将會詳細介紹微服務的幾個方面,讨論一些諸如服務發現、服務部署選擇以及重構單體應用到微服務的的話題。

後記

第一篇文章翻譯完了,說實話膽顫心驚,第一次作大死,厚着臉皮上吧!!!

作者:nonumber1989

上一篇: 微服務
下一篇: 微服務