微服務(wù)(一)

微服務(wù)

微服務(wù)技術(shù)現(xiàn)在獲得了很多的關(guān)注:論文,博客,社交媒體的討論和會議展示。他們在加德納技術(shù)成熟度曲線(Gartner Hype Cycle)上迅速的駛向巔峰。與此同時,軟件社區(qū)的懷疑者認為微服務(wù)沒有什么新東西。有些反對者聲稱微服務(wù)只是重新定義SOA(Service-Oriented Architecture)而已。然而,拋開夸張的宣傳和懷疑言論,微架構(gòu)模式有很多優(yōu)點,特別是它使敏捷開發(fā)和交割復(fù)雜企業(yè)應(yīng)用成為可能。

本系列會討論設(shè)計,構(gòu)建和部署微服務(wù)。你將會學(xué)習(xí)實現(xiàn)微服務(wù)的途徑以及比較它和傳統(tǒng)單一架構(gòu)模式(Monolithic Architecture)的區(qū)別。這個系列會描述各種不同的微服務(wù)架構(gòu)元素,是否適合于你的項目,以及怎樣應(yīng)用它。

首先我們考慮為為什么你會考慮使用微服務(wù)。

構(gòu)建單體架構(gòu)應(yīng)用

讓我們假設(shè)你打算開始構(gòu)建一個全新的出租車呼叫應(yīng)用,目標(biāo)是和Uber和Hailo競爭。經(jīng)過些前期的會議和需求整理,你打算創(chuàng)建一個新的項目,這個項目要么手動生成或是用一些平臺工具產(chǎn)生,比如Rails,Spring Boot,Play或Maven。這個新的應(yīng)用會有一個模塊(Hexagonal Architecture)??如圖1-1

應(yīng)用的核心是商業(yè)邏輯,它是被不同的模塊實現(xiàn),這些模塊中定義服務(wù),領(lǐng)域?qū)ο蠛褪录?。核心的外圍是一些適配器,通過這些適配接口和外圍交互。適配器的例子包括數(shù)據(jù)庫訪問組件,信息交互組件(產(chǎn)生和消費消息)和Web組件,Web組件要么暴露API要么實現(xiàn)用戶界面(UI)。

盡管擁有邏輯模塊架構(gòu),這個應(yīng)用仍然被打包成單一整體部署。實際的格式依賴于應(yīng)用的語言和框架。例如,許多的Java應(yīng)用打包成WAR文件并部署到應(yīng)用服務(wù)器上,如Tomcat或Jetty。其他的Java應(yīng)用打包成自我包含的可執(zhí)行JARs。相似地Rails和Node.js應(yīng)用打包成一個目錄層次結(jié)構(gòu)。

這種單一架構(gòu)的應(yīng)用非常常見。他們?nèi)菀组_發(fā),因為我們的IDE和其他工具適于構(gòu)建單一架構(gòu)應(yīng)用。這類的應(yīng)用也易于測試。你可以簡單地啟動應(yīng)用和測試界面利用Selenium實現(xiàn)端到端的測試。單一體的應(yīng)用易于部署。你僅需要拷貝打包過的應(yīng)用到服務(wù)器上。你可以運行多個應(yīng)用副本,通過負載均衡器擴容應(yīng)用。在項目的前期這種方式工作的很好。

圖1-1 一個簡單的司機呼叫應(yīng)用

駛向單體架構(gòu)地獄

不幸的是,這種簡單途徑有非常嚴重的缺陷。成功的應(yīng)用會有一個慣性,就是隨著時間的推移,它會成長變得非常龐大。在每一迭代期(Sprint),你的開發(fā)組實現(xiàn)一些用戶需求(User Story),當(dāng)然了,這意味著增加很多行代碼。經(jīng)過幾年的時間。你的小的,簡單的應(yīng)用會成長成異常龐大的單一體(Monstrou Monolith)。舉個極端的例子,最近我和一個開發(fā)人員聊天,他正在寫一個工具,用于分析幾百萬行代碼的應(yīng)用和成千上萬JARs之間的依賴關(guān)系。我確信為了開發(fā)這樣一個野獸級的應(yīng)用,肯定會用掉很多開發(fā)人員幾年的工作時間。

一旦你的應(yīng)用變成龐大和復(fù)雜的單一體架構(gòu),你的開發(fā)組織肯能會在痛苦中。任何嘗試敏捷開發(fā)和交割都會很掙扎。一個主要的問題是這個應(yīng)用異常復(fù)雜。幾乎不可能讓單一一個開發(fā)者完全理解它的全部。這就會使修復(fù)缺陷(Bug)和實現(xiàn)新的功能(new features)變得困難和耗時。更進一步,這是下降中的漩渦。如果基本代碼難于理解,那么代碼更不易更改。你將會陷于麻煩和不能理解的泥沼中。

應(yīng)用的大小也會減慢開發(fā)速度。應(yīng)用越大,它的啟動時間越長。我統(tǒng)計過開發(fā)者關(guān)于單一體應(yīng)用軟件大小和性能的關(guān)系,有些啟動用時12分鐘。我也聽說過花費40分鐘啟動應(yīng)用的例子。如果開發(fā)者經(jīng)常地重啟應(yīng)用服務(wù)器,那么他們一天的大部分時間在等待,開發(fā)效率肯定很低。

體量大,復(fù)雜的單體式應(yīng)用的另一個問題是部署困難?,F(xiàn)在,以現(xiàn)在的技術(shù)狀態(tài),像SaaS這類的應(yīng)用,一天會被更新幾次。這種方式對于單體式應(yīng)用的部署會異常的困難,因為即使小的更新,你必須重新部署整個應(yīng)用。耗時的啟動時間也是件麻煩事。通常地,因為更新沒有理解清楚,很可能你需要大量的說動測試區(qū)驗證。結(jié)果會導(dǎo)致持續(xù)部署幾乎也是不可能任務(wù)。

當(dāng)不同模塊資源需求矛盾時,單一體架構(gòu)的應(yīng)用也很難擴容。舉例說,一個模塊或許實現(xiàn)耗計算資源的(CPU-Intensive)圖片處理邏輯,那么理想情況下是應(yīng)當(dāng)部署到Amazon計算優(yōu)化的實例(EC2 Compute Optimized)上去。另外一個模塊或許是內(nèi)存數(shù)據(jù)庫,那么最好是應(yīng)該部署到內(nèi)存優(yōu)化的實例中去(Memory-Optimized Instances)。然而,以為這些模塊被封裝到了一起,你不得不折中你的硬件選擇。當(dāng)然應(yīng)用就無法調(diào)整到最優(yōu)。

單體應(yīng)用的另外一個問題是可靠性。因為所有的模塊運行于同一進程中,任何一個模塊中的缺陷(Bug),比如內(nèi)存泄漏,有可能使整個進程崩潰。更進一步,因為所有這個進程的實例是一一樣的,這個缺陷將會影響整個應(yīng)用的使用。

最后但同等重要的是,單體架構(gòu)應(yīng)用很難采用新的架構(gòu)和語言。比如,讓我們假設(shè)你的應(yīng)用用XYZ框架寫成,大約有2百萬行代碼。這會令用ABC框架重寫你的應(yīng)用變的異常昂貴(時間和成本),即使ABC框架好很多。這結(jié)果導(dǎo)致當(dāng)你想采用新技術(shù)刷新(TEC Refresh)你的應(yīng)用時候,不得不面對這巨大的障礙。在這個項目的開始時,你會被困在框架和架構(gòu)選擇上。

總結(jié)一下,你有個成功的商業(yè)應(yīng)用,它已成長為體量巨大的單體,很少有開發(fā)人員能完全理解它。它用一些陳舊的不是很高產(chǎn)的技術(shù)實現(xiàn),這讓雇傭非常有才能的開發(fā)人員變異常困難。這個應(yīng)用很難擴容并且不可靠。結(jié)果導(dǎo)致敏捷開發(fā)和交割變得幾乎不可能。

那么,接下來,我們該怎么做?

微服務(wù)-處理復(fù)雜性

很多的組織,如Amazon,eBay和Netfix公司,已經(jīng)用微服務(wù)架構(gòu)模式(Microservice

Architecture Pattern)解決了這個問題。不去構(gòu)建體量龐大的單體式應(yīng)用,而是分解你的應(yīng)用成更小的,相互連接的服務(wù)。

一個服務(wù)通常實現(xiàn)一組獨特的功能,比如,訂單管理,客戶管理等。每個微服務(wù)是一個迷你小應(yīng)用(mini-application),每個這樣的應(yīng)用都有自己的架構(gòu)(Hexagonal Architecture)包括各種適配器實現(xiàn)的商業(yè)邏輯。有些微服務(wù)會提供API給其他的微服務(wù)和應(yīng)用的客戶端。其他微服務(wù)或許或?qū)崿F(xiàn)Web界面。在運行態(tài),每個實例通常是虛擬機器(VM)或是Docker容器。

舉例說來,一個可能的前面提到系統(tǒng)的分解如下圖1-2

圖1-2? 單體應(yīng)用分解成微服務(wù)

應(yīng)用的每個功能區(qū)域現(xiàn)在是被它自己的微服務(wù)實現(xiàn)。更進一步講,在出租車呼叫服務(wù)系統(tǒng)中,每個Web應(yīng)用被分解成一組更小的Web應(yīng)用-比如一個是為乘客的應(yīng)用,一個是為司機的應(yīng)用。這種分解簡化了部署并提供給特定用戶,設(shè)備和特定的用例獨特的體驗。

每個后端服務(wù)暴露一組REST API接口,并且大多數(shù)的服務(wù)都調(diào)用其他的服務(wù)。舉例說,司機管理服務(wù)(Driver Management)調(diào)用通知服務(wù)(Notification)告知一個可用的司機關(guān)于一個可能的行程(TRIP)。UI服務(wù)為了渲染頁面調(diào)用其他的服務(wù)。服務(wù)或許用異步的,基于消息通信。服務(wù)之間通訊在以后提供更多的細節(jié)。

一些REST API也暴露給司機和乘客移動App。然而,這些APP不直接訪問后端的服務(wù),而是通過中中間的API網(wǎng)關(guān)訪問。API網(wǎng)關(guān)負責(zé)負載均衡,緩存,訪問控制,API測量和監(jiān)控。API網(wǎng)關(guān)可以借助于NGINX技術(shù)實現(xiàn)。下一章節(jié)將會討論API網(wǎng)關(guān)的細節(jié)。

微服務(wù)架構(gòu)模式相當(dāng)于規(guī)模方塊(Scale Cube)的Y軸擴容,規(guī)模方塊是非常優(yōu)秀的”擴容藝術(shù)“(The Art of Scalability )這本書中提出一種3D擴容能力模型。

其他的兩個擴容軸是X軸和Z軸,X軸擴容相當(dāng)于運行多個相同應(yīng)用副本,通過負載均衡器均衡訪問。Z軸擴容(數(shù)據(jù)分區(qū)),一個請求的屬性(如,行的主鍵或是客戶的ID)用于路由這個請求到一個特定的服務(wù)器。

應(yīng)用通常會同時采用這三種擴容方式。Y軸擴容分解應(yīng)用成微服務(wù),如圖1-3。


圖1-3 規(guī)模方塊3D擴容模型

在運行時,X軸擴容運行多個服務(wù)實例,每個服務(wù)通過服務(wù)均衡器提供吞吐量和可用性。有些應(yīng)用或許也會用Z軸擴容分區(qū)服務(wù)。圖1-4展示行程管理服務(wù)如何用Docker容器部署在Amazon EC2。

在運行時態(tài),行程管理服務(wù)包括多個服務(wù)實例。每個服務(wù)實例是一個Docker容器。為了高可用,容器運行在多個云虛擬主機上(VM)。服務(wù)實例的前端是負載均衡器比如NGINX,均衡器在實例之間分配服務(wù)請求。均衡器或許也會處理像緩存,存取控制,API測量和監(jiān)控等。

圖1-4 用Docker 部署行程管理服務(wù)

微服務(wù)架構(gòu)模式極大地影響應(yīng)用和數(shù)據(jù)庫之間的關(guān)系。不是不同的服務(wù)分享同單一個數(shù)據(jù)庫,而是每個服務(wù)有自己的數(shù)據(jù)庫模式。另外,這種途徑和企業(yè)級數(shù)據(jù)模型的想法是不相合的。它會導(dǎo)一些致數(shù)據(jù)的重復(fù)。然而,每個服務(wù)擁有自己數(shù)據(jù)庫是能從微服務(wù)獲益的關(guān)鍵所在,因為它確保了服務(wù)之間松度耦合。圖1-5數(shù)據(jù)庫架構(gòu)應(yīng)用的樣例。


圖1-5 司機呼叫系統(tǒng)的數(shù)據(jù)庫架構(gòu)

表面上看,微服務(wù)架構(gòu)模式類似于SOA。兩種架構(gòu)都包含一組服務(wù)。然而,一種說法是微服務(wù)架構(gòu)沒有商業(yè)化,Web服務(wù)規(guī)范(ws-*)和企業(yè)服務(wù)總線(Enterprise Service Bus-ESB)包袱?;谖⒎?wù)的應(yīng)用傾向于簡單,輕量級協(xié)議比如REST,而不是WS-*。這種架構(gòu)自身中也盡量避免使用類ESB總線功能。微服務(wù)架構(gòu)模式也拒絕使用SOA的其他部分,比如數(shù)據(jù)訪問的規(guī)范架構(gòu)模式(Canonical Schema)。

微服務(wù)優(yōu)點

微服務(wù)架構(gòu)模式有很多的優(yōu)點。首先,它能處理復(fù)雜的問題。它分解體量龐大單體應(yīng)用成一組服務(wù)。總功能保持不變,同時,應(yīng)用已經(jīng)被分解成可控的服務(wù)。以RPC(Remote

Procedure Call)驅(qū)動或是消息驅(qū)動(Message-Driven)API方式提過服務(wù),每個服務(wù)有自己很好的邊界定義。微服務(wù)強化了模塊級別,這些是在單體架構(gòu)基礎(chǔ)代碼很難取得的隔離。因此,單個服務(wù)開發(fā)更快,更容易理解和維護。

第二點,這種架構(gòu)使每個服務(wù)被一個Team獨立的開發(fā),這個Team可以專注于這個服務(wù)上。提供了服務(wù)的協(xié)議,開發(fā)人員可以任意選擇合理的技術(shù)去實現(xiàn)。當(dāng)然了,大多數(shù)的開發(fā)組織為避免完全混亂會限制一些技術(shù)選擇。然而,這種自由意味著開發(fā)者不必繼續(xù)沿用在項目開始時存在的過時的技術(shù)。當(dāng)寫一個新的服務(wù),他們能選擇用當(dāng)下流行的技術(shù)。更近一步,因為每個服務(wù)相對很小,用當(dāng)下的技術(shù)重寫一個過時的服務(wù)是可行的。

第三,微服務(wù)架構(gòu)模式使每個微服務(wù)可以被獨立的部署。服務(wù)的本地更新,開發(fā)人員不需要協(xié)調(diào)部署其他的服務(wù)。這類更改被測試后可以立即被部署。比如UI組可以執(zhí)行A|B測試,并迅速地迭代UI的變化。微服務(wù)架構(gòu)使持續(xù)部署成為可能。

最后,微服務(wù)架構(gòu)的每個服務(wù)可以被獨立的擴容。你能部署合理的實例數(shù)量,以滿足容量和可用性約束的需求。更進一步,你可以用最合理的硬件配置滿足服務(wù)對資源的需求。比如,為計算量大的圖片處理服務(wù)可以部署到EC2計算優(yōu)化(Compute Optimized Instance)的實例上去,并且部署內(nèi)存數(shù)據(jù)庫到EC2內(nèi)存優(yōu)化(Memory-Optimized)的實例上去。

微服務(wù)的缺點

正如大約30年前,F(xiàn)red Brooks在”人月神話“(The Mythical Man-Month)里寫到,沒有”銀彈“可以解決所有問題。像其他的技術(shù)一樣,微服務(wù)架構(gòu)模式也有缺陷。一個缺陷是它自己的名字。術(shù)語”微服務(wù)“過度的強調(diào)服務(wù)的大小。事實上,一些開發(fā)者擁抱構(gòu)建極端的10-100行代碼的服務(wù)。盡管小服務(wù)是可取的,但一定要記住小服務(wù)是一種手段但這不是它的主要的目標(biāo),微服務(wù)的目標(biāo)是最大限度地分解應(yīng)用以便于敏捷開發(fā)和部署。

微服務(wù)另一個缺點是源于這樣一個事實,微服務(wù)應(yīng)用自身是分布式的。開發(fā)人員需要選擇和實現(xiàn)基于RPC或消息的進程間通信機制。還有就是,開發(fā)者必須寫代碼處理局部失敗,因為一個請求的目的地或許不可用或慢。盡管他們不是什么高不可測的高科技(Rocket Science),但是他們比單體架構(gòu)更復(fù)雜,單體模塊間的調(diào)用是語言基本的方法或程序調(diào)用。

微服務(wù)的另一個挑戰(zhàn)是分區(qū)的數(shù)據(jù)庫架構(gòu)。商業(yè)交易中更新多個實體是相當(dāng)普遍的操作。這類的交易的實現(xiàn)在單體模式下是很簡單的。因為他們只需處理一個數(shù)據(jù)庫。而在以微服務(wù)為架構(gòu)的應(yīng)用中,你需要更新多個數(shù)據(jù)庫,這些數(shù)據(jù)庫是屬于不同的服務(wù)的。采用分布式交易事務(wù)通常不是一個選項,不僅是因為CAP理論(CAP Theorem)的限制。而是因為他們基本不被現(xiàn)在的NOSQL數(shù)據(jù)庫和消息代理器所支持。你不得不采用一種最終基于一致性的方法(Eventual Consistency-based Approach)去解決,不過這種方法對開發(fā)者來說有些復(fù)雜。

測試一個微服務(wù)架構(gòu)的應(yīng)用是更復(fù)雜。比如,以現(xiàn)在的框架如Spring Boot,寫一個測試案例,它啟動一個單體Web應(yīng)用測試其RESP API是件很簡單事。相反的相似的測試案例去測試基于微服務(wù)的應(yīng)用就會很復(fù)雜,因為你不得不啟動這個服務(wù)和它依賴的服務(wù),要么就是需要寫一些代碼模擬其他服務(wù)。一樣地,這也不是什么高級技術(shù),但是也不能低估實現(xiàn)其的復(fù)雜性。

微服務(wù)架構(gòu)還有一個挑戰(zhàn)是實現(xiàn)一些跨服務(wù)的更改。比如,讓我們想象你去實現(xiàn)一個故事(Story),它需要更改A,B和C服務(wù),其中A依賴于B,B依賴于C。在單體應(yīng)用中,你可以簡單的更新相關(guān)的模塊,集成這些更改,并且一次部署應(yīng)用就好。相反地,在微服務(wù)架構(gòu)應(yīng)用中,你需要小心翼翼計劃并且協(xié)調(diào)每個服務(wù)的修改。舉例來說,你需要更新服務(wù)C,然后服務(wù)B,最后服務(wù)A。幸運的是大多數(shù)的更改通常只會影響一個服務(wù);并且多服務(wù)更改的需求相對來說比較少見。

部署一個基于微服務(wù)架構(gòu)的應(yīng)用也是很復(fù)雜的。單體架構(gòu)應(yīng)用借助于負載均衡器僅需要部署多個實例到相同配置的多個服務(wù)器上。用數(shù)據(jù)庫和消息代理器地址配置每個應(yīng)用實例。而一個微服務(wù)的應(yīng)用通常包含很多的服務(wù)。比如,按照Adrian Cockcroft Hailo有160個不同的服務(wù),Netflix有超過600個服務(wù)。

每個服務(wù)將會有多個運行實例。有很多的動態(tài)部分需要配置,部署,擴容和監(jiān)控。另外,你也需要實現(xiàn)“服務(wù)發(fā)現(xiàn)機制”,它使一個服務(wù)能發(fā)現(xiàn)需要與其通信的其他服務(wù)的地址(主機地址和端口號)。傳統(tǒng)的基于票據(jù)和手工操作的方法不能伸縮地解決這種級別的復(fù)雜性。因此,成功地部署微服務(wù)應(yīng)用需要開發(fā)者更多的控制和高級別自動化機制。

一種自動化的方法是采用現(xiàn)成的PaaS平臺比如Cloud Foundry。PaaS平臺提供給開發(fā)者一種簡便的方法部署和管理他們的微服務(wù)。它幫助開發(fā)者簡化了獲取和配置IT資源流程。同時,配置系統(tǒng)和網(wǎng)絡(luò)的專家可以確保最佳實踐和公司的政策一致。

另外一種自動化部署微服務(wù)的方法是開發(fā)你自己的PaaS系統(tǒng)。一種典型的開始點是采用集群解決方案,比如Kubernetes,結(jié)合容器技術(shù)如Docker協(xié)同解決問題。后面我們會深入看基于軟件應(yīng)用的交接方法,如Nginx,它能輕易地在微服務(wù)的級別處理緩存,訪問控制,API測量和監(jiān)控等問題。

總結(jié)

構(gòu)建復(fù)雜應(yīng)用本來就很困難。單體架構(gòu)模式僅適用于簡單,輕量級的應(yīng)用。如果你應(yīng)用這種架構(gòu)在復(fù)雜應(yīng)用中,你注定會步入到痛苦的世界中。盡管微服務(wù)架構(gòu)有它自己的缺點和實現(xiàn)的挑戰(zhàn),它仍然是實現(xiàn)復(fù)雜,演進應(yīng)用比較好選擇。

后面的章節(jié),我們深入微服務(wù)架構(gòu)模型的細節(jié)中,討論如服務(wù)發(fā)現(xiàn),服務(wù)部署選項和重構(gòu)單體應(yīng)用到微服務(wù)的策略。

翻譯自“Microservice -from design to deployment"? by? Chris Richardson with Floyd Smith.

(待續(xù)第二章....)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容