微服务架构(Microservices)
說在前面
好久沒寫博文了,心里癢癢(也許是換工作后,有點時間了吧)。最近好像談論微服務的人比較多,也開始學習一下,但是都有E文,看起來半懂不懂的。 Martinfowler的《 微服務》,也算是入門必讀了。有人 翻譯過,但是只有一半。還是自己練練手吧。微服務
“微服務架構”一詞在過去幾年里廣泛的傳播,它用于描述一種獨立部署的軟件應用設計方式。這種架構方式并沒有非常準確的定義,但是在業務能力、自動部署、端對端的整合、對語言及數據的分散控制上,卻有著顯著特征。 “微服務”----只不過在滿大街充斥的軟件架構中的一新名詞而已。盡管我們非常鄙視這樣的東西,但是這玩意所描述的軟件風格,越來越引起我們的注意。在過去幾年里,我們發現越來越多的項目開始使用這種風格,以至于我們身邊的同事在構建企業應用時,把它理所當然的認為這是一種默認開發形式。然而,很不幸,微服務風格是什么,應該怎么開發,關于這樣的理論描述卻很難找到。 簡而言之,微服務架構風格,就像是把小的服務開發成單一應用的形式,每個應用運行在單一的進程中,并使用如HTTP這樣子的輕量級的API。這些服務滿足某需求,并使用自動化部署工具進行獨立發布。這些服務可以使用不同的開發語言以及不同數據存儲技術,并保持最低限制的集中式管理。 開始介微服務風格前,先介紹整體風格:即把一個完整的應用當成一開發單元。企業應用通常包含三個部分:客戶端界面(由HTML、Javascript組成,使用瀏覽器進行訪問)、數據庫(由許多的表組件構成一個通用的、相互關聯的數據管理系統)、服務端應用。服務端應用處理HTTP請求、執行領域邏輯、檢索并更新數據庫中的數據、使用適當的HTML視圖發送給客戶端。服務端應用是完整的 ---- 由單一的邏輯層次執行。系統中任務變更都會導到服務端的應用重新編輯并發布一個新的版本。 這樣的整體服務是這樣的構建系統的很自然的方式。雖然利用開發語基礎特性會把應用封裝成類、函數、命名空間,但是業務中所有邏輯都要在單一的進程中處理完成。在某些場景中,開發者可能在的筆計本中開發、測試應用,然后利用部署通道來保證經過正常測試、發布的修改內容正確的發布的產品中。也可以使用橫向擴展,通過負載均橫系統將事個應用部署到多臺服務器上。 整體風格的應用也是相當成功的,但是越來越多的人感覺到有點不妥,特別是在云中進行應用的發布時。變更發布周期被綁定了 ---- 原來可以劃分成小的應用、小的需要的變更,需要統一的進行編譯和發布。隨著時間的推移,人們通常難于維護一種優美的模塊化的結構,使得一個模塊的變更很難不會影響到其它的模塊。進行擴展,也需要進行整體的擴展,而不能根據進行部分的擴展。 圖1:整理架構與微服務架構這些原因導致了微服務架構風格的出現:以服務構建應用。因為服務可以獨立部署、獨立擴展,服務也可以提供模塊化的邊界,并且不同的使用也可以使用不同的開發語言。服還可以以不同的周期進行管理。 微服務風格關不是我們發明的,也不是一個新的東西,它至少起源于Unix時代的設計原則。之所以這樣,我們認為只是當時很少人考慮過這種風格,并認識到把軟件使用這種的風格可以帶來更多的好處。
微服務風格的特性
我們沒有辦法對微服務風格進行準確的定義,但是我們可以償試著描述一下微服務風格所應該具有的覺特性,這樣就可以對它打上相應的標簽了。正如其它定義中對特性的描述一新,并不是所有的微服務風格都要所有的特性,但是我們認為常見的微服務都應該有這些特性。盡管我們是相當松散的社區核心成員,但是我們也計劃償試描述我們工作中或者在其它我們了解的組件中所理解的微服務。當然,我們并不依賴于那些已經明確過的定義。組件與服務
自從我們從事軟件行業以來,一直希望能夠構建由組件組成的系統,就像我們所看到的實現世界由物件構成的一樣。在過去的幾十年里,我們已經看到了大部分語言平臺的公共庫的進行了精簡,并取得可觀的進展。 當我們談論組件的時候,有可能會因為組件的不同定義引起混亂。因此我們申明,這里談到的 組件是指軟件中獨立的單元,它能獨立替代和獨立更新。 微服務架構也使用組件庫,但是它把軟件拆分成服務,并認為這是主要的組織形式。我們把 組件庫定義為程序中相互關系、并使用內存中調用的組件,把 服務定義為進程間使用如Web請求服務或者遠程調用來相互通信的組件。(這種定義的方式與其它面向對象程序中服務對象的概念是不一樣的。) 把服務當成組件(而不是組件庫)一個主要的原因是服務可以獨立的部署。如果你的應用由多個組件庫組成并跑在一個進程中,那么任何組件的變更都將導致整體應用的重新發布。但是如果由許多服務構成的應用,你可以想像的到每個服務的變更僅需要發布相應的服務。當然,這也不是絕對的,比如導致服務接口的變更的更新就需要相應服務的變化,但優秀微服務構架,會盡量避免這種服務間的耦合并完善服務的交互接口。 把服務當成組件的另一個考慮是這將擁有更新清晰的接口。許多開發語言并沒有良好的定義公共接口的機制。通常只有文檔和規范說明,讓用戶避免組件間會導致組件耦合的過度的依賴。不過服務由是是通過明確的遠程接口調用,這個問題就很容易解決了。 使用服務也有它的不足之處。遠程調用比進制內部調用更消耗性能,而且遠程的API比較粗糙,難以使用。如果由于對組件的職責進行變更,影響到跨進程間的交互,那么這操作起來也比較困難。 第一個可能的特性,我們看到每個服務是運行在獨立的進程上的。注意,這只是第一個可能的特性。服務也可以由多個進程組成,它們是同時開發和部署的,例如服務可能用到一個應用進制和一種數據稟。圍繞業務功能進行組織
當尋找把一個大的應用拆分成小的部分時,主管通常注意在技術層面,拆分成UI組、服務邏輯組和數據庫組。當使用這種標準對團隊進行劃分時,甚至小小的更變都將導致跨團隊間項目協作,從而消耗時間和預算審批。一個高效的團隊會針對這種情況進行改善,關注它們所涉及的應用邏輯,并從中做出較好的選擇。換句話說,邏輯無處不在。Conway's Law的實踐就是一個例子。 [plain]? view plain copy圖3:通過團隊邊界強調服務邊界 www.comparethemarket.com就采用這種組織形式。不同職能的團隊同時為各自的產品構建和運營負責,同時每個產品又拆分成多個通過消息引擎通信的單獨服務。 大型的整體型應用也可以按照業務功能進行模塊化的,盡管這種例子不常見。當然,我們敦促一個大型的團隊將一個構建成整體型的應用依照業務功能進行拆分。我們能看到主要問題在于,這種組件形式會導致很多的上下文依賴。如果在大量的模塊邊界上都存在這種大量的調用,對于團隊的每個成員來說,短期內是很難記住的。此外,我們發現模塊化方式需要大量的規范去強制執行,當然,大量明確的拆分也讓服務組件在團隊的邊界中更加清晰。
產品不是項目?
大部分的軟件開發者都使用這樣的開發模式:至力于提供一些被認為是完整的軟件。一旦開發完成,軟件將移交給維護部門,然后,開發組就可以解散掉了。 微服務的支持者認為,這種做法是不可取的,并提議開發組應該負責產品的整個生命周期。一個常見的證明是:Amazon的“你編譯,你運維(you build, you run it)”的理念,它要求開發團隊對軟件產品的整個生命周期負責。這要求開發者每天都關注軟件產品的運行情況,并與用戶聯系的更緊密,同時承擔一些售后支持。 成熟的產品會與業務功能進行綁定。除了把軟件看成既定功能的集合外,會進一步關心“軟件如何幫助用戶實現業務功能”這樣的問題。 采用整體型的架構并不是沒有原因的,但是越小的服務粒度越容易促進用戶與服務提供商之前的關系。強化終端及弱化通道
當構建不同的進程間通信機制的時候,我們發現有許多的產品和方法能夠把更加有效方法強加入的通信機制中。比如企業服務總線(ESB),這樣的產品提供更有效的方式改進通信過程中的路由、編碼、傳輸、以及業務處理規則。 微服務傾向于做如下的選擇:強化終端及弱化通道。微服務的應用致力松耦合和高內聚:采用單獨的業務邏輯,表現的更像經典Unix意義上的過濾器一樣,接受請求、處理業務邏輯、返回響應。它們更喜歡簡單的REST風格,而不是復雜的協議,如WS或者BPEL或者集中式框架。 有兩種協議最經常被使用到:包含資源API的HTTP的請求-響應和輕量級消息通信協議。最為重要的建議為: [plain]? view plain copy分散治理
集中治理的一種好處是在單一平臺上進行標準化。經驗表明這種趨勢的好處在縮小,因為并不是所有的問題都相同,而且解決方案并不是萬能的。我們更加傾向于采用適當的工具解決適當的問題,整體式的應用在一定程度上比多語言環境更有優勢,但也適合所有的情況。 把整體式框架中的組件,拆分成不同的服務,我們在構建它們時有更多的選擇。你想用Node.js去開發報表頁面嗎?做吧。用C++來構建時時性要求高的組件?很好。你想以在不同類型的數據庫中切換,來提高組件的讀取性能?我們現在有技術手段來實現它了。 當然,你是可以做更多的選擇,但也不意味的你就可以這樣做,因為你的系統使用這種方式進行侵害意味著你已經有的決定。 采用微服務的團隊更喜歡不同的標準。他們不會把這些標準寫在紙上,而是喜歡這樣的思想:開發有用的工具來解決開發者遇到的相似的問題。這些工具通常從實現中成長起來,并進行的廣泛范圍內分享,當然,它們有時,并不一定,會采用開源模式。現在開源的做法也變得越來越普遍,git或者github成為了它們事實上的版本控制系統。 Netfix就是這樣的一個組織,它是非常好的一個例子。分享有用的、尤其是經過實踐的代碼庫激勵著其它的開發著也使用相似的方式來解決相似的問題,當然,也保留著根據需要使用不同的方法的權力。共享庫更關注于數據存儲、進程內通信以及我們接下來做討論到的自動化等這些問題上。 微服務社區中,開銷問題特別引人注意。這并不是說,社區不認為服務交互的價值。相反,正是因為發現到它的價值。這使得他們在尋找各種方法來解決它們。如 Tolearant Reader和 Consumer-Driven Contracts這樣的設計模式就經常被微服務使用。這些模式解決了獨立服務在交互過程中的消耗問題。使用Consumer-Driven Contracts增加了你的信心,并實現了快速的反饋機制。事實上,我們知道澳大利亞的一個團隊致力使用Consumer-Drvien Contracts開發新的服務。他們使用簡單的工程,幫助他們定義服務的接口。使得在新服務的代碼開始編寫之前,這些接口就成為自動化構建的一個部分。構建出來的服務,只需要指出這些接口適用的范圍,一個優雅的方法避免了新軟件中的'YAGNI '困境。這些技術和工具在使用過程中完善,通過減少服務間的耦合,限制了集中式管理的需求。 也許分散治理普及于亞馬遜“編譯它,運維它”的理念。團隊為他們開發的軟件負全部責任,也包含7*24小時的運行。全責任的方式并不常見,但是我們確實發現越來越多的公司在他們的團隊中所推廣。Netfix是另外一個接受這種理念的組件。每天凌晨3點被鬧鐘吵醒,因為你非常的關注寫的代碼質量。這在傳統的集中式治理中這是一樣多么不思議的事情呀。分散數據管理
對數據的分散管理有多種不同的表現形式。最為抽象層次,它意味著不同系統中的通用概念是不同的。這帶來的覺問題是大型的跨系統整合時,用戶使用不同的售后支持將得到不同的促銷信息。這種情況叫做并沒有給用戶顯示所有的促銷手段。不同的語法確實存在相同的詞義或者(更差)相同的詞義。 應用之間這個問題很普遍,但應用內部這個問題也存在,特別是當應用拆分成不同的組件時。對待這個問題非常有用的方式為 Bounded Context的領域驅動設計。DDD把復雜的領域拆分成不同上下文邊界以及它們之間的關系。這樣的過程對于整體架構和微服務框架都很有用,但是服務間存在著明顯的關系,幫助我們對上下文邊界進行區分,同時也像我們在業務功能中談到的,強行拆分。 當對概念模式下決心進行分散管理時,微服務也決定著分散數據管理。當整體式的應用使用單一邏輯數據庫對數據持久化時,企業通常選擇在應用的范圍內使用一個數據庫,這些決定也受廠商的商業權限模式驅動。微服務讓每個服務管理自己的數據庫:無論是相同數據庫的不同實例,或者是不同的數據庫系統。這種方法叫 Polyglot Persistence。你可以把這種方法用在整體架構中,但是它更常見于微服務架構中。圖4:Polyglot Persistence 微服務音分散數據現任意味著管理數據更新。處理數據更新的常用方法是使用事務來保證不同的資源修改數據庫的一致性。這種方法通常在整體架構中使用。 使用事務是因為它能夠幫助處理一至性問題,但對時間的消耗是嚴重的,這給跨服務操作帶來難題。分布式事務非常難以實施,因此微服務架構 強調服務間事務的協調,并清楚的認識一致性只能是最終一致性以及通過補償運算處理問題。 選擇處理不一致問題對于開發團隊來說是新的挑戰,但是也是一個常見的業務實踐模式。通常業務上允許一定的不一致以滿足快速響應的需求,但同時也采用一些恢復的進程來處理這種錯誤。當業務上處理強一致性消耗比處理錯誤的消耗少時,這種付出是值的的。
基礎設施自動化
基礎設施自動化技術在過去幾年中得到了長足的發展:云計算,特別是AWS的發展,減少了構建、發布、運維微服務的復雜性。 許多使用微服務架構的產品或者系統,它們的團隊擁有豐富的 持集部署以及它的前任 持續集成的經驗。團隊使用這種方式構建軟件致使更廣泛的依賴基礎設施自動化技術。下圖說明這種構建的流程: 圖5:基本的構建流程 盡管這不是介紹自動部署的文章,但我們也打算介紹一下它的主要特征。我們希望我們的軟件應該這樣方便的工作,因此我們需要更多的自動化測試。流程中工作的軟件改進意味著我們能自動的部署到各種新的環境中。 整體風格的應用相當開心的在各種環境中構建、測試、發布。事實證明,一旦你打算投資一條整體架構應用自動化的的生產線,那么你會發現發布更多的應用似乎非不那么的可怕。記住,CD(持續部署)的一個目標在于讓發布變得無趣,因此無論是一個還是三個應用,它都一樣的無趣。 另一個方面,我們發現使用微服務的團隊更加依賴于基礎設施的自動化。相比之下,在整體架構也微服務架構中,盡管發布的場景不同,但發布工作的無趣并沒有多大的區別。圖6:模塊化部署的區別
容錯性設計
使用服務作為組件的一個結果在于應用需要有能容忍服務的故障的設計。任務服務可能因為供應商的不可靠而故障,客戶端需要盡可能的優化這種場景的響應。跟整體構架相比,這是一個缺點,因為它帶來的額外的復雜性。這將讓微服務團隊時刻的想到服務故障的情況下用戶的體驗。Netflix的 Simian Army可以為每個應用的服務及數據中心提供日常故障檢測和恢復。 這種產品中的自動化測試可以讓大部分的運維團隊正常的上下班。這并不意味著整體構架的應用沒有這么精巧的監控配置,只是在我們的經驗中它并不常見。 由于服務可以隨時故障,快速故障檢測,乃至,自動恢復變更非常重要。微服務應用把實時的監控放在應用的各個階段中,檢測構架元素(每秒數據庫的接收的請求數)和業務相關的指標(把分鐘接收的定單數)。監控系統可以提供一種早期故障告警系統,讓開發團隊跟進并調查。 對于微服務框架來說,這相當重要,因為微服務相互的通信可能導致緊急意外行為。許多專家車稱贊這種緊急事件的價值,但事實是這種緊急行為有時是災難。監控是至關重要的,它能快速發現這種緊急不良行為,讓我們迅速修復它。 整體架構,跟微服務一樣,在構建時是通明的,實情上,它們就是這樣子的。它們不同之處在于,你需要清楚的認識到不同進程間運行的服務是不相關的。庫對于同一進程是透明的,也因此不那么重要了。 微服務團隊期望清楚的監控和記錄每個服務的配置,比如使用儀表盤顯示上/下線狀態、各種運維和業務相關的指標。對斷路器(circuit breaker)狀態、目前的吞吐量和時延細節,我們也會經常遇到。設計改進
微服務實踐者,通常有不斷改進設計的背景,他們把服務分解成進一步的工具。這些工具可以讓應用開發者在不改變速度情況下,控制都他們的應用的需求變更。變更控制不意味首減少變更,而是使用適當的方式和工具,讓它更加頻繁,至少,很好讓它變得可控。 不論如何,當你試圖軟件系統拆分成組件時,你將面臨著如何拆分的問題。那么我們的決定拆分我們應用的原則是什么呢?首要的因素,組件可以被獨立替換和更新的,這意味著我們尋找的關鍵在于,我們要想象著重寫一個組件而不影響它們之前的協作關系。事實上,許多的微服務小組給它進一步的預期:服務應該能夠報廢的,而不是要長久的發展的。 Guardian網站就是這方面的一個優秀的例子,它初期被設計和構建成一個整體架構,但它已經向微服務的發展了。整體構架仍然是它網站的核心,但是他們使用微服務來增加那些使用整體架構API的新特性。這種方法增加這些臨時的特性非常方便,比如運動新聞的特稿。這樣站點的一個部分可以使用快速的開發語言迅速整合起來,當它過時后可以一次性移除。我們發現一家金融機構用相似的方法增加新的市場營銷活動,數周或者幾個月后把它撤銷。 可代替是模塊化開發中的一個特例,它是用模塊來應對需要變更的。你希望讓變更是相同模塊,相同周期中進行變化而已。系統的某些很小做變更部分,也應該放在不同的服務中,這樣它們更容易讓它們消亡。如果你發現兩個服務一直重復的變更時,這就是一個要合并它們的信號了。 把組件改成服務,增加了細化發布計劃的一個機會。整體構架的任務變更需要整個應用的完整的構建和發布。然而,使用微服務,你只需要發布你要修改的服務就可以了。這將簡化和加速你的發布周期。缺點是你需要為一個變更服務發布可能中斷用戶的體驗而擔心。傳統的集成方法是使用版本來處理這些問題,但是微服務 版本僅是最后的通告手段。我們需要在設計服務時盡可能的容忍供應商的變更,以避免提供多個版本。其它
微服務系統多大?
盡管“微服務”一詞在架構風格中越來越流行,它的名字很不辛讓人關注它的服務大小,以及對“微”這個組成的爭議。在我們與微服務實踐者的談話中,我們發現了服務的大小范圍。被報道的最大團隊遵循亞馬遜Tow Pizaa團隊理念(比如,一個團隊吃兩個比薩就可以了。),這意味著不超過20號(一打)人。我們發現最小配置是半打的團隊支撐起一打的服務。 這也引發這樣的考慮:規模為一個服務一打人到一個服務一個人的團隊打上微服務的標簽。此刻我們認為,它們是一樣的,但是隨著對這種風格的深入研究,也存在我們改變我們的想法的可能。微服務與SOA
當前我們談到微服務時,通常會問,這是不是我們20年前討論的面向服務架構(SOA)。這是一個很好的觀點,因為微服務風格也SOA所提倡的一些優勢非常相似。盡管如此,問題在于SOA意味的 太多不同的東西了,因此通常時候我們談的所謂“SOA”時,它與我們談論的風格不一至,因為它通常是指在整體風格應用中的ESB。 此外,我們發現面向服務的風格是這么的拙劣:從試圖使用ESB隱藏復雜性, 到使用多年才認識到發費數百美元卻沒產生任務價值這樣的失敗,到集中治理模式抑制變更。而且這些問題往往很難發現。 可以肯定的時,微服務社區中使用的許多的技術都開發者是從大型機構的整合服務經驗中發展來的。 Tolerant Reader模式就是這樣的一個例子。由于互聯網的發展,利用簡單的協議這種方法,讓它從這些經驗傳達的出來。這是從已經很復雜的集中式標準中的一種反模式,坦白的說,真讓人驚嘆。(無論何時,當你需要用一個服務來管理你的所有的服務,你就知道這很麻煩。) SOA的這種常見行為讓微服務的提倡者拒絕打上SOA的標簽,盡管有人認為微服務是從SOA中發展而來的,或許面向服務是對的。無論如何,事實上SOA表達這么多的含義,它給一個團隊清醒的認識到這種構架風格就已經值的了。多語言,多選擇
JVM做為一個平臺,它的增長就是一個平臺中運行多語言的最大的例子。過去二十年中,它通常做為更高層次語言的殼,以達到更高層次的抽象。比如,研究它的內部結構,、使用低級的語言寫更高效的代碼。盡管如此,許多整體風格并不需要這種層次的性能優化或者在語法及高層次上的抽象,這很常見(讓我們很失望)。此外整體構架通常意味著使用單一的語言,這也限制著使用技術的數量。實踐標準和強制標準
它有點尷尬,微服務團隊傾向于避免這種通常由企業架構隊伍定制的僵硬的強制標準,但是它們卻非常樂于甚至推廣這些開放的標準,如HTTP、ATOM、其它微規范。 關鍵的不同在這些標準是怎么開發出來的,以及它們是怎么被推廣的。標準被一些組件管理,如IETF認證標準,僅當它們在互聯網上有幾個在用的實現,通常源自于開源工程的成功應用。 這些標準單獨分離出來,與那種在企業中通常有沒有什么編碼經驗的或者沒有什么影響力的廠商標準進行區別。讓做對事更容易
一方面,我們發現在持續發布、部署越來越多的使用自動化,是很多有用的工具開發出來幫助開發者和運營商的努力結果。為打包、代碼管理、支撐服務的工具,或者增加標準監控的記錄的工具,現在都非常常見了。網絡中最好的,可能就是 Netflix's的開源工具,但是包含 Dropwizard在內的其它工具也被廣泛的使用著。斷路器(circuit breaker)和產品中現有的代碼
斷路器(circuit breaker)出現在《 Realease It!》一書中,與Bulkhead和Timeout這樣的模式放在一起。實施起來,這些模式用于構建通信應用時相當的重要。 Netflix的博客在解釋它們的應用時,做了大量的工作。同步是有害的
任務時候,你在服務間的調用使用同步的方法,都會遇到宕機時間的乘積效應。簡單的說,你的系統宕機時間是你系統的單獨組件的宕機時間的乘積。你面臨的選擇使用異步或者管理宕機時間。在www.guardian.co.uk中,它們在新平臺中使用一種簡單的規則來實現它:在Netflix中每次用戶請求的同步調用,他們重新設計的平臺API都會把它構建成異步的API來執行。微服務是未來嗎?
我們寫這篇文章的主要目的在于解釋微服務的主要思想和原則。但是發時間做這事的時候,我們清醒的認識到微服務構架風格是一個非常重要的想法:一個值得企業應用中認真考慮的東西。我們最近使用這種風格構建了幾個系統,認識那些也使用和喜歡這種方法的愛好者。 我們認識的使用這種方式的先行者,包含亞馬遜、Netflix、The Guardian、The UK Government Digital Service、realestate.com.au、Forward和comparethemarket.com。2013看的巡回會議充滿了向正在想成為微服務一分子的公司,包含Travis CI。此外,大量的組件正在從事我們認為是微服務的事,只是沒有使用微服務的名字而已。(通常,它們被打上SOA的標簽,盡管,我們認為SOA有許多不同的地方。) 盡管有這些積極的經驗,然后,我們也不急于確認微服務是未來軟件架構方向。至今為止,我們的經驗與整體風格的應該中相比出來的是有優勢的,但是我們意識知這樣的事實,我們并沒有足夠的時間來證明我們的論證。 你所使用的架構通常是你開發出來后,使用的幾年的實際成果。我們看到這些工程是在一個優秀的團隊,帶著對模塊化的強烈追求,使用在過去幾年中已經衰退的整體架構構建出來的。許多人相信,這種衰退不太可能與微服務有關,因為服務邊界是清晰的并且很難再完善的。然而,當我們還沒看到足夠多的系統運行足夠長時間時,我們不能肯定微服務構架是成熟的。 當然,還有原因就是,有人期望微服務構架不夠成熟。在組件化方面的任何努力,其成功都依賴于軟件如何拆分成適合的組件。指出組件化的準確邊界應該在那,這是非常困難的。改良設計要承認邊界的權益困境和因此帶來的易于重構的重要性。但是當你的組件是被遠程通信的服務時,重構比進程內的庫又要困難的多。服務邊界上的代碼遷移是困難的,任務接口的變更需要參與者的共同協作,向后兼容的層次需要被增加,測試也變更更加復雜。 另一個問題在于,如果組件并沒有清晰的劃分,你的工作的復雜性將從組件內部轉向組件間的關系。做這事不僅要圍繞著復雜,它也要面對著不清晰和更難控制的地方。很容易想到,當你在一個小的、簡單的組件內找東西,總比在沒有關系的混亂的服務間要容易。 最后,團隊技能也是重要的因素。新的技術傾向于被掌握更多的技能的團隊使用。但是掌握多技能的團隊中使用的技巧在較少技能的團隊中并不是必需的。我們發現大量的少技能的團隊構建混亂的整合構架,但是它要發時間去證明使用微服務在這種情況下會發生什么。一個糟糕的團隊通常開發糟糕的系統:很難說,微服務在這種情況下是否能幫助它們,還是破壞它們。 一個理性的爭議在于,我們聽說,你不應該從微服務構架開始做。最好從整體構架開發,做模塊化開發,然后當整體構架出現問題是再把模塊化拆分成服務。(盡管這種建議不是好主意,因為一個好的進程內接口并不是一個好的服務接口。) 因此我們持這種謹慎的樂觀。到目前為止,我們還沒有足夠認識,關于微構架能否被大范圍的推廣。我們不能肯定的說,我們要終結什么,但是軟件開發的挑戰在于你只能在不完整的信息中決定你目前要處理的問題。【案例】互聯網保險O2O平臺微服務架構設計
?????? 關于架構,筆者認為并不是越復雜越好,而是相反,簡單就是硬道理也提現在這里。這也是微服務能夠流行的原因,看看市場上曾經出現的服務架構:EJB、SCA、Dubbo等等,都比微服務先進,都比微服務功能完善,但它們都沒有微服務這么深入民心,就是因為他們過于復雜。簡單就是高科技,蘋果手機據說專門有個團隊研究如何能讓用戶更加簡單的操作。大公司都是由小公司發展起來的,如果小公司在開始技術選型時感覺某個框架費時費力就不會選擇,而小公司發展到大公司的過程,一般也伴隨著系統不斷優化的過程,而不斷優化往往不會重新選擇開發技術和框架,而是在原來基礎改進,這也許就是簡單框架流行的本質。?
假設我們需要為超高業務量的保險代理企業設計一個“互聯網+”保險平臺。假設這家保險代理企業網上保險注冊用戶規模為2千萬,門店及加盟商銷售人員2萬,年保單量2億單(中國平安總用戶規模達1.67億,擁有超過79.8萬名壽險銷售人員和約24.6萬名正式雇員。截至2015年6月30日,集團總資產達4.63萬億元,歸屬母公司股東權益為3,311.90億元。而目前互聯網保險領頭羊眾安保險,經營以小額貸款為主,由于背靠阿里巴巴,日保單銷售量可達1億,不過別人很難復制眾安保險的模式)。因此我們取大型互聯網企業和眾安保險的折衷來考慮這個保險O2O平臺。
l 需求分析
參考保險業務相關文檔(文檔不全),獲得如下核心需求矩陣(因為涉及功能太多,只取大的功能點)。
?
| 分類 | 功能 | 質量 | 約束 |
| 電子商務(B2C) | 產品展示(搜索、詳情展示等) | 及時響應、安全性、健壯性、易用性 | 多種險種,處理方式可能不同 |
| ? | 產品購買(提交訂單、支付) | 及時響應、安全性、健壯性、易用性 | 多種險種,處理方式可能不同 |
| ? | 用戶中心(我的保單、我的理賠等) | 及時響應、安全性、健壯性、易用性 | 多種險種,處理方式可能不同 |
| 代理人管理(加盟商管理) | 車險投保(詢價、錄單、繳費) | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
| ? | 非車險投保(詢價、錄單、繳費) | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
| ? | 保單查詢 | 及時響應、健壯性、可擴展性 | ? |
| ? | 單證管理 | 及時響應、健壯性、可擴展性 | ? |
| ? | 我的賬戶(我的保單、傭金結算等) | 及時響應、安全性、可靠性、易用性 | 多種險種,處理方式可能不同 |
| 案卷管理 | 案卷錄入 | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
| ? | 索賠資料收取 | 及時響應、健壯性、可擴展性 | ? |
| ? | 案卷交接 | 及時響應、健壯性、可擴展性 | ? |
| ? | 案卷跟蹤 | 及時響應、健壯性、可擴展性 | ? |
| 客戶管理 | 客戶信息維護 | 及時響應、健壯性、可擴展性 | 上傳大量文件 |
| ? | 客戶活動管理 | 及時響應、健壯性、可擴展性 | ? |
| ? | 商機管理 | 及時響應、健壯性、可擴展性 | ? |
| ? | 我的工作臺(消息、活動、商機) | 及時響應、健壯性、可擴展性 | ? |
| 保險公估 | 車險定損過程跟蹤協助 | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
| ? | 人傷出險協助 | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
| ? | 法律援助服務 | 及時響應、健壯性、可擴展性 | 多種險種,處理方式可能不同 |
?
從O2O的概念來看:“O2O即Online To Offline,也即將線下商務的機會與互聯網結合在了一起,讓互聯網成為線下交易的前臺。這樣線下服務就可以用線上來攬客,消費者可以用線上來篩選服務,還有成交可以在線結算,很快達到規模。該模式最重要的特點是:推廣效果可查,每筆交易可跟蹤(百度百科)”,不是每一種服務都適合O2O。商品類的銷售不適合O2O,因為你直接從網店就可以購買根本不需要線下店。但保險類服務的確需要線下和線上結合,如果是純線上將不能滿足客戶的服務需求。O2O的線下服務可以是加盟商、代理人,也可以是直營店。
上面的需求是按照用戶角度提出的,雖然使用“系統”一詞,但這里的系統是一個抽象概念,可能包括軟件系統以及人事、制度等在內。上面的需求可以分為三大類,一類是針對終端用戶純線上服務:電子商務網站(B2C);一類是專門針對代理人服務的:代理人系統;剩下的是一些公共服務,有可能電子商務網站和代理人系統都會用到的一些服務。另外保險業務最大的一個問題是多個險種,不同險種處理方式有很大不同,這跟普通電子商務網站區別很大,比如天貓,所有商品都是同樣的下單方式,同樣售后服務(主要就是快遞這塊),而保險產品即使是線上B2C網站下單操作,短險、壽險、車險等也是不同的,更何況保險有一大堆售后服務,這些售后服務更加不相同。傳統保險公司在處理這方面時,一般會做多個系統,比如壽險系統,車險系統等等。
l 系統分析
安裝之前提到的業務規模我們分析(假設)出一些比較重要的性能需求:
a)產品方面:繼續上線當前沒有的保險產品
b)B2C網站日訪問量:5000萬PV
c)B2C產品購買并發高峰:2000 TPS
d)運維系統同時在線:1萬(共有2萬銷售員或代理人)
e)運維系統并發高峰:2000 TPS
f)短險訂單:每年1.85億單
g)長險訂單:每年500萬
h)車險訂單:每年1000萬
i)案卷信息:每年新增100萬單
日訪問量5000萬和產品并發2000 TPS是我們假設的,客戶信息和案卷信息是隨訂單數據量變化而變化,在前面我們雖然假設了總共每年產生2億個訂單,但是根據保險種類,短險(旅游險、傷殘險)明顯產生了90%的訂單量,這一點需要特殊處理。除此之外車險和長險(主要指壽險等)無論是投保還是售后服務都有明顯不同,所以也需要特殊處理。
那么我們按照上面的需求,進行系統分析,首先按大的職責將職責相同的劃分為一個服務。并且有了上面這個性能需求,所有功能需求都需要增加一項“質量”特性,那就是“高并發”,高并發會影響到所有設計。另外如果要將互聯網保險平臺質量特性排個序,最重要的是可擴展性、安全性,因為保險的種類多而且處理方式不同,除此之外,高并發和可靠性也會直接影響功能的實現,但并沒有可擴展性影響大。深入分析職責后把每一種功能的實現關鍵技術列出,如下:
| 需求分類 | 實現需求 | 實現子系統及服務 | 軟硬件實現技術 |
| 客戶端 | B2C電子商務網站 | B2C Web客戶端 | 集群部署、高速緩存、分布式緩存、搜索引擎技術、靜態化 |
| B2C電子商務網站手機客戶端 | B2C App客戶端 | ? | |
| 代理人管理 | 代理人Web客戶端 | 集群部署、高速緩存、分布式緩存、搜索引擎技術、靜態化 | |
| 代理人管理手機客戶端 | 代理人App客戶端 | ? | |
| 案卷處理管理 | 案卷處理Web客戶端 | 集群部署、分布式緩存 | |
| 客戶管理管理 | 客戶管理Web客戶端 | 集群部署、分布式緩存 | |
| 保險公估管理 | 保險公估Web客戶端 | 集群部署、分布式緩存 | |
| 運維產品管理 | 產品管理Web客戶端 | 集群部署、分布式緩存 | |
| 報表及財務統計 | 報表及財務統計Web客戶端 | 集群部署、分布式緩存 | |
| 公共服務 | 運維產品管理、Web前端產品訪問 | 產品服務 | 集群部署、分布式緩存 |
| 電子商務或代理人訂單管理 | 訂單服務 | 集群部署、分布式緩存 | |
| 電子商務或代理人等涉及財務操作 | 總賬服務 | 集群部署、分布式緩存 | |
| 報表及財務統計 | 報表服務 | 集群部署、分布式緩存 | |
| 業務服務 | B2C電子商務網站及手機客戶端個人賬戶 | B2C個人賬戶服務 | 集群部署、分布式緩存 |
| 代理人管理 | 代理人管理服務 | 集群部署、分布式緩存 | |
| 案卷處理管理 | 案卷處理管理服務 | 集群部署、分布式緩存 | |
| 客戶管理 | 客戶管理服務 | 集群部署、分布式緩存 | |
| 保險公估管理 | 保險公估管理服務 | 集群部署、分布式緩存 | |
| 短險開放式接入 | 開放式接入平臺服務 | 集群部署、分布式緩存 | |
| 工具性服務 | 保險公司產品對接 | 產品對接服務 | 集群部署、消息隊列 |
| 在線支付 | 第三方支付服務 | 集群部署 | |
| 短信郵件通知 | 通知服務 | 集群部署、消息隊列 | |
| 性能監控 | 日志采集服務 | 集群部署、消息隊列 | |
| 文件服務器 | 文件服務 | 集群部署、消息隊列 | |
| 服務授權與審計 | 服務授權與審計服務 | 集群部署 | |
| 分布式事務管理 | 分布式事務管理服務 | 集群部署 | |
| 定時任務管理 | 定時任務服務 | 集群部署 |
?
?????? 各個子系統及模塊的關系如下圖。
?
?
?????? 其中訂單服務、產品服務、財務服務、工具服務為基礎服務,其它各個業務模塊的服務會調用這些基礎服務。各個業務模塊的服務都是根據業務領域進行劃分的,同一業務領域下實現技術不同會被劃分為兩個服務,比如產品展示和訂單原本屬于同一個大的領域,但其因為實現技術和質量要求不同需要劃分為兩個服務。因為短險接入量大,而且大部分是跟第三方合作接入,因此設計短險接入公共接口服務平臺處理大量短險訂單。
?
l 存儲及緩存架構
對于大型的高并發系統來講,最重要的當屬數據的架構。我們在前面也提到過,web系統業務處理模塊本身就可以集群部署,當用戶出現高并發時最先遇到的瓶頸就是數據庫訪問的瓶頸。這也是我們說數據架構最為重要的原因。其實web系統是典型的“計算機信息系統”,也就是說一切以數據(信息)為基礎,所有的功能都是圍繞著數據來的。這也是我們在這里說數據是web系統最重要的,很多公司在做少用戶量web系統時直接設計好數據庫就可以開發了。
按照上面劃分的業務領域我們設計多個數據庫,技術選項包括“是否讀寫分離”、“是否水平切分”及“路由鍵”。其中路由鍵是指在進行水平切分后,我們使用那個“標識”去查詢數據庫,一般來說會使用該業務領域聚合根對象的主鍵作為路由鍵。
| 數據庫 | 是否讀寫分離 | 是否水平切分 | 水平切分路由鍵 |
| 產品數據庫 | 是 | ? | ? |
| 訂單數據庫 | ? | 是 | 客戶ID |
| 公共數據庫(元數據、公共數據) | 是 | ? | ? |
| 客戶管理數據庫 | ? | 是 | 客戶ID |
| 案卷管理數據庫 | ? | 是 | 客戶ID |
| 代理人服務數據庫 | ? | 是 | 代理人ID |
| B2C電子商務個人賬戶數據庫 | ? | 是 | 客戶ID |
| 保險公估數據庫 | ? | 是 | 客戶ID |
| 工具數據庫(短信、支付、文件) | ? | 是 | 客戶ID |
| 報表數據庫 | 是 | ? | ? |
| 日志采集服務數據庫 | ? | 是 | 日志ID |
?
?????? 除工具數據庫外,其它的數據庫的劃分很容易理解。工具數據庫的數據也大都跟客戶或說用戶有關,比如“產品對接服務”,產品對接服務是指用戶在購買了保單后,系統會自動對接到具體的保險公司接口去上傳保單信息和下載保單,所以水平切分數據庫時可以采用用戶ID作為路由鍵。“在線支付”和“通知服務”也是類似,都是保存用戶相關的數據,在線支付服務保存的是用戶在線支付的流水,通知服務保存的是發送給某用戶的短信或郵件。
?????? 另外在數據架構中我們也可以看到一個規律,就是使用了水平分庫的存儲結構就不能再使用讀寫分離,原因是防止存儲單元過度泛濫,因為使用了水平分庫之后,本身也要為數據庫建立多個備份庫,這個時候如果再用讀寫分離需要建立一套只讀庫,數據庫的數量將增加一倍。使用了分庫后我們可以把熱點數據存儲在分布式緩存中以起到讀寫分離類似的作用。
另外緩存也是存儲技術的一種,而且非常重要。從日常生活中我們也可以看到這一點。比如你要去購買一臺電腦,你會發現二級緩存和內存大的價格高出很多,如果你的顯卡顯存巨大,那將是頂級配置,發燒級配置。手機也是一樣,內存大的手機速度明顯快,價格也高,如果內存和持久化存儲都高,那也是頂級配置。對于web系統也是一樣,有些大型web系統只要緩存處理的好,數據庫不需要分庫就可以承載億級的用戶,比如維基百科、新浪微博等。也因此,不管是電子商務網站還是互聯網+大型應用,都會在它們的架構中看到緩存大量使用的情況。
從整個web系統的架構來看,緩存在兩個層面大量使用,分別是展示層和邏輯層,展示層通常使用高速的頁面緩存,邏輯層通常使用高并發的分布式緩存。當然有些分布式緩存工具既可以在邏輯層使用也可以在顯示層使用。
| 系統 | 是否使用CDN | 是否使用高速緩存服務器(varnish) | 是否使用分布式緩存(redis) |
| B2C電子商務網站 | 是 | 是 | ? |
| 報表及財務統計Web端 | ? | 是 | ? |
| B2C個人賬戶服務 | ? | ? | 是 |
| 代理人管理服務 | ? | ? | 是 |
| 案卷處理管理服務 | ? | ? | 是 |
| 客戶管理服務 | ? | ? | 是 |
| 保險公估管理服務 | ? | ? | 是 |
| B2C個人賬戶服務 | ? | ? | 是 |
| 短險公共平臺服務 | ? | ? | 是 |
?
l 邏輯架構
在給出系統總體的邏輯架構前,我們先看看系統前端和服務層直接的關系。前端是客戶端,可以有多個客戶端,也可以有多種客戶端,比如手機端(APP、WAP、微信)等。客戶端和服務之間的架構是典型的MVC架構,V是客戶端,C就是spring mvc或servlet開發的控制層,對于Web應用V和C在開發角度是一個工程,剩下的M就是服務層。這是對于web系統來說的,對于app或者使用html直接實現的客戶端,或者是.net實現的桌面客戶端,由于這些客戶端是單獨開發的,沒有控制層,因此需要增加一層“API 網關”作為這些客戶端的控制層。
?
圖中的服務組件就是指各個業務服務,這些服務可以看成是組件的概念。通常前端界面一個界面中需要的數據通常不僅僅來自于同一個服務,即使是來自于同一個服務,那也來自于很多不同的接口,servlet或api 網關作為控制層,所起到的作用就是組合接口、組合數據,并處理接口間的事務性。另外服務組件也是分層的,圖中并沒有展現,一般可以分為3層,從低到高依次是工具性服務組件、基礎業務層服務組件、業務層服務組件。前端界面的請求按照從高到底向下傳遞和處理請求。
按照職責、通用性、技術特性綜合考慮和計量,邏輯架構設計如下圖:
?
十幾個子系統分別分布在服務層、控制層、表現層(典型的三層架構)。實體層和接口訪問層雖然屬于“層”,但它們并不單獨發布,而是使用Jar包類庫的方式提供給其它服務調用,是邏輯上的層。服務組件的構成大都是按照業務領域劃分的,只有一個除外,就是“B2C網站”,B2C網站由于是面向終端用戶的高并發電子商務網站,為了處理高并發,我們將其拆分為兩個業務領域(也可以拆分成多個業務領域,看實際并發量),分別是用戶賬戶和產品。用戶瀏覽產品并購買,這是電子商務網站最基本的兩個領域。其中產品瀏覽等功能由更底層的產品服務提供,用戶賬戶功能由會員服務提供。還有一些功能,比如推薦商品可能是由其它服務提供,這些可以在控制層直接組合這些服務。
?
l 服務架構
服務框架采用典型的“服務注冊表”模式,注冊表使用redis。服務組件在啟動時將自己注冊進服務注冊表,web服務器或api 網關在訪問服務時查詢服務注冊表得到服務的uri,然后調用某服務接口。
?
?
?????? 淘寶的dubbo使用的是zookeeper作為服務注冊表,之所以使用zookeeper,主要是使用它的負載均衡的功能。筆者認為restful接口沒有必要使用zookeeper做負載均衡,可以使用nginx(負載均衡服務器都可以),所以沒必要選用動態的zookeeper作為注冊表,而是使用“redis+心跳監測”機制(redis也可以換為LDAP等)來完成服務注冊和監控失效服務的功能。這個方案至少比dubbo簡單幾個數量級,簡單就是硬道理。
在注冊服務時只需要注冊nginx服務器的IP以及服務描述信息即可。反觀dubbo還要注冊接口進注冊表,筆者認為這個沒必要,因為調用一個服務接口的充分必要條件就是知道服務器的IP即可。至于調用什么服務接口,肯定在代碼里已經寫死,目標服務器必定存在這個服務接口。將服務接口注冊進服務注冊表如果是為了監控審計服務的使用情況,那這個功能使用訪問日志來實現能做的更好。
總體的分布式拓補結構如下圖:
?
?????? 對于服務集群來講,看上去像是一個大哥(nginx)帶有眾多小弟的結構。訪問某服務群組只需要訪問其nginx服務器即可,這里nginx均采用高可用方案,防止單臺nginx出現問題。至于高層服務調用底層服務也是直接訪問其服務器組的nginx。這個執行的流程其實和日常生活中的概念很像,如公司內部的執行流也是如此:老板分配任務給部門經理,部門經理分配任務給主管,主管分配任務給具體的個人(某單臺服務器)。領導們起到的作用也是負載均衡和監控,負載均衡就是把任務可以分配給多個人同時執行(并且某個執行失敗自動切換),監控就不必說了就是監控任務完成情況。在我們的方案里,會專門有個服務去監控所有服務器的執行情況,很簡單的服務就可以做到。
l 關于分布式事務
如果研究一下EJB就會發現,EJB和微服務的架構基本相同,甚至所有面向服務(SCA、SOA等等)的架構都相差不大。很多人反對EJB,并不是EJB不夠強大,而是它不夠簡單,它給項目帶來的復雜性甚至超過了項目本身(這也是筆者不建議使用dubbo框架的原因)。曾有人說過:你要么把事情做的盡可能簡單,讓人挑不出毛病;要么把事情做的盡可能復雜,讓人找不出毛病,EJB就是后者。EJB分布式事務機制實現的很好,可惜的是這種“一刀切”的事務機制,大大降低了web系統的性能,所以幾乎所有面向互聯網的應用都極少使用分布式事務,也就不會采用EJB。互聯網應用本身事務性操作并不多,一些新聞、博客之類的網站甚至都不需要事務。另外一個方面,即使出現一個或兩個分布式事務應用場景,也可以通過其它手段解決,比如事件機制等等。
在之前的文章中我們也提到過對于分布式事務最佳的策略是盡量避免。如果避免不了將按以下方式實現。
1)? 將分布式事務性操作封裝在一個服務中,這個服務使用XA或鏈式分布式事務管理。
2)? 可以使用事件機制協調事務管理(具體做法就是事務失敗后發失敗事件到可持久化的消息隊列,然后需回滾操作的接口監控此事件并執行回滾)。
3)? 使用自定義分布式事務管理器管理分布式事務。
?
筆者設想的自定義分布式事務管理器主要是封裝了流程及分布式事務相關功能,筆者將在其它文章專門討論。如圖所示,假設有一個事務需要依次調用ABCDE五個接口,我們首先調用分布式事務管理接口創建這條“流程”的實例,實例中五個接口分別對應五個狀態,調用成功后將該接口對應的狀態設置為“成功”,反之就是“失敗”,流程處理結束后,分布式事務檢查狀態,然后按照一定的策略調用失敗接口的反向操作接口去回滾數據(前提條件也是參與分布式事務操作的接口要開發反向操作接口)。這既是一個簡單的流程引擎,也是一個分布式事務協調處理裝置,具體是否有必要做的復雜(比如處理并行流程),還要看實際環境下分布式事務的情況,但筆者認為互聯網前端應用使用簡單流程應該足以應付。
??
l 開發架構
系統所需的工程,“[ ]”里面表示工程的名稱。
?
從圖中不難看出,我們將運維相關前端界面合并為一個前端系統,總體來講前端只有3種,B2C前端、APP前端、運維前端。把運維前端合并為一個項目有利于加快前期的開發、部署的效率,在后期如果某子系統功能界面太多,可以將前端系統獨立,比如公估系統、客戶管理系統等,獨立后的系統需要使用單點登錄,這樣就可以在各個系統之間免登陸切換。
開發環境:
編碼:UTF-8??????????????????????????????????????????
工具:Myeclipse 10
SVN:Site-1.8.22
Web服務器:Tomcat7
JDK: JDK1.7、 Java EE 5
開發環境:Maven 3
?
開發技術選型:
表現層:Bootstrap+Html+Jquery
MVC框架:Spring MVC 3.2
安全框架:Spring security 3.2
Rest接口實現:Spring MVC Rest
持久層:Mybatis3.2
分布式緩存:Redis
數據庫:MySql 5.6
【拓展】使用容器技術來建立一個微服務架構
在之前的博文中,我講解了Linux容器技術的相關實現,比如如何使用Docker來建立流線型的開發和測試體驗。因為可以實現跨不同類型基礎設施的兼容(比如,在AWS上,容器也可以如實體服務器上一樣輕松的運行),容器讓代碼的部署異常便捷。在實際工作中,測試和開發環境的細微不同很可能會導致應用程序的部署失敗;因此在這種情況下,對于開發和測試工作,容器技術可以讓開發者豁免很多預想之外的工作和相互推脫。
在本篇文章中,我們將討論是什么特性讓容器技術如此適應開發和測試工作,同樣適用于在AWS平臺上構建一個基于微服務的架構。對于Web應用程序來說,微服務架構可以讓應用程序的代碼庫更加敏捷,并且容易管理。下面,我們將介紹這個架構為何可以大幅提升開發者生產效率的原因,并了解它能夠快速迭代和擴充一個代碼庫的原理。對于快速發展中的創業公司來說,微服務架構可以讓開發團隊在研發過程中更加的敏捷和靈活。
Web開發簡史
首先,我們先簡潔地回顧下20年內基于Web開發的歷史,它可以讓我們知悉微服務架構為什么可以在Web開發領域如此的盛行,同時也順便了解這個架構可以解決的問題。
在Web應用程序開發的早期,應用程序通常使用Common Gateway Interface(CGI)建立,這個接口為網絡服務器提供了處理瀏覽器發來的HTTP請求時執行腳本(通常情況下用Perl編寫)的能力。CGI的擴展性非常很好,因為它需要為每個輸入請求都建立一個Perl進程。為了解決這個問題,那個時代的網絡服務器通常都會添加模塊化的支持。Apache,現下最為流行的網絡服務器之一,增加了“mod_perl”讓Perl代碼可以在內部運行,這樣一來,CGI腳本就可以在更少的時間內執行。
即使對比傳統的CGI類似mod_perl這些技術有了很大的提升,但仍然存在問題。也就是說,負責視圖層(比如,在HTML頁面上執行一個動態模塊)的代碼通常會被混入應用程序邏輯代碼中。這就意味著,完成一個簡單的任務,比如在HTML列表中增加一列,或者在form中增加一個元素,通常需要修改一個低等級的應用程序代碼。因此,Web程序開發技術下一個階段中衍生了“server pages”,它允許在HTML嵌入執行代碼。這樣一來,應用程序邏輯代碼與視圖代碼被很好的分離。在Java開發領域,一個被稱為“Model 2”的設計模式得以快速演變,在這里,應用程序代碼放到Java servlets中,數據則通過Java Beans進行,視圖層邏輯則使用了Java server pages,詳見下圖:
圖1:Model 2設計模型
隨后,在Java領域,“Model 2”模式在很短的時間就演化成了“Model-View-Controller(MVC)”框架,比如Apache Struts。而在其他領域,Ruby on Rails則非常盛行。在MVC模式中,控制器類會定義方法,通過“route”類映射成URL模式被調用。 控制器方法會利用“model”類封核心應用程序實體的業務邏輯和數據。最后,每個控制方法都會渲染一個“view”用于顯示,并修改相應模式類的方法。在這種模式下,業務、應用程序、視圖邏輯被很好的分離,如圖2:
圖2:MVC設計模型
REST協議的盛行
就在MVC被廣泛接受并成為網絡開發途徑的同時,進程間通信(IPC)也開始利用上了基于文本的序列化格式,比如XML和JSON。而在類似SOAP這些協議實現跨HTTP IPC的不久后,網絡開發已不再限制于給瀏覽器建立交付內容的應用程序,為其他程序執行操作和交付數據的網絡服務也逐漸走上歷史的舞臺。這種基于服務的架構擁有非常強大的功能,因為它消除了代碼庫共享的依賴性,從而開發者可以更進一步的解耦應用程序組件。而SOAP協議和相關的WS-*標準也變得越來越復雜,并更加依賴于應用程序服務器的實現,至此開發者開始投身更為輕量級的REST協議。同時,隨著移動設備的劇增,Web UX development逐漸走向AJAX和JavaScript框架,應用程序開發者開始廣泛使用REST在客戶端設備和網絡服務器之間做數據傳輸。
后來人們發現,MVC框架同樣非常適合開發REST端點。REST面向資源的特性被很好的映射成了控制器和模型理念,如圖3所示:
圖3:MVC的REST端點
Monolithic架構
因此,曾今由模型、視圖層、控制器組成,主要用于給應用程序交付HTML內容的MVC應用程序發生了本質上的變化——它們不僅可以支撐傳統的HTML,也可以通過REST端點來支撐JSON。應用程序被部署為一個單一的文件(比如Java)或者同一個目錄下的文件合集(比如Rails)。然而不容忽視的是,所有應用程序代碼都運行在相同的進程中。因此在縮放過程中,開發者需要將應用程序代碼的多個副本部署到多個所需的服務器上。下圖解析了Monolithic架構:
圖片4:Monolithic架構
在Monolithic架構中存在著很多的問題。首先,隨著應用程序的功能和服務越來越多,代碼將變得越來越復雜。對于新的開發者來說,這一點非常頭疼。新型集成開發環境在加載、編譯整個應用程序代碼時也可能存在問題,同時這個過程的耗時也可能非常長。此外,因為所有程序代碼都運行在服務器上的相同進程中,導致應用程序某個組成的擴展也非常難。如果某個服務是內存密集型的,而另一個是CPU密集型的,那么服務器必須有足夠的內存和CPU來滿足每個服務的需求。因此,鑒于每個服務器都使用非常高的CPU和內存,基礎設施的整體花費可能會非常高,特別是在縱向擴展策略下。最后非常微妙的是,應用程序的組成通常也會映射到研發團隊的結構上。UX工程師負責UI組件的建立,中間層開發者通常負責建立服務器端點,而數據庫工程師和DBA們則負責數據訪問組件和數據庫。如果某個UX工程師期望給增加一些顯示,他往往需要來自中間層和數據庫工程師的配合。就像水一樣,人們通常期望以最少的阻力執行,每個工程師也都期望為其負責的應用程序嵌入盡可能多的邏輯。鑒于這些問題,隨著時間的推移,代碼將越來越難以管理。
微服務架構
微服務架構的發明就是用來解決這些問題。定義在Monolithic架構應用程序中的服務將拆分成獨立的服務,它們在不同的主機上進行獨立的部署。
圖片5:微服務架構
每個微服務都對應了一個獨立的業務功能,也只定義了該功必須的一些操作。這聽起來比較類似面向服務架構(SOA),事實上,微服務架構和面向服務的架構確實有很多共同的特性。兩個架構都使用服務的模式組織代碼,兩種架構在不同的服務間都建立了非常明確的邊界。然而,面向服務的架構起源于Monolithic應用程序交互的需求,通常彼此都會提供一個API(基于SOAP)。在面向服務架構中,集成重度依賴于中間件,特別在企業服務總線(EBS)中。微服務架構通常會利用一個消息總線,但是無論任何情況在消息層都不會存在邏輯——它純粹的被用于服務之間的交互。這與ESB有著非常顯著的差別,ESB包含了大量邏輯——用于消息路由、模式驗證、消息翻譯和業務規則。因此,對比傳統的面向服務架構,微服務架構往往更為簡單,不會包含用于定義服務間接口的同級別控制和規范化數據建模。通過使用微服務,開發將非常快速,服務的衍變也只需匹配業務的需求。
微服務架構的另一個核心優勢就是服務可以基于資源的需求進行獨立擴展。取代運行包含大量CPU和內存的大服務器,微服務可以被部署在更小的主機上,這些主機只需要滿足其部署服務的需求。同時,開發者可以根據業務的需求選擇開發語言,比如:一個圖像處理服務可以使用類似C++這樣的高性能語言實現,一個執行數學或者靜態操作的服務可以使用Python實現,對資源進行增刪查改的基礎操作則往往通過Ruby進行。在微服務中,開發者并不需要考慮Monolithic架構中使用的“一刀切”模型——比如只使用MVC框架和單一的編程語言。
然而,不容忽視的是,微服務同樣存在一些劣勢。因為服務通常部署在多個主機上,很難持續跟蹤指定服務究竟運行在某臺主機上。同時,因為微服務架構使用的主機容量往往小于Monolithic架構,隨著微服務架構不停的橫向擴展,主機數量將以一個非常恐怖的速度增長。在AWS環境中,微服務架構中獨立服務需要的資源往往會小于最小的EC2實例類型。從而造成了超量配置并浪費開銷。此外,如果服務使用不同的編程語言將開發,這就意味著每個服務的部署都需要完全不同的庫和框架,從而服務的部署非常復雜。
容器的用武之地
Linux容器技術的使用可以很大程度上緩解微服務架構所帶來的問題。Linux容器技術使用了類似cnames和namespaces這樣的內核接口,它允許不同容器共享相同的內核,同時容器之間還進行了完全的隔離。Docker執行環境使用了一個被稱為libcontainer的模塊,它標準化了這些接口。Docker同樣為容器鏡像提供了一個類GitHub的資源庫DockerHub,讓容器的共享和發布非常簡單,也正是這種相同主機上的容器隔離簡易了不同語言開發的微服務代碼部署。使用Docker,我們可以創建一個DockerFile來描述所有用到的語言、框架和服務間庫的依賴性。舉個例子,下面代碼中的DockerFile可以用來定義一個微服務的Docker鏡像,它使用了Ruby和Sinatra框架:
FROM ubuntu:14.04 MAINTAINER John Doe <jdoe@example.com> RUN apt-get update && apt-get install -y curl wget default-jre git RUN adduser --home /home/sinatra --disabled-password --gecos '' sinatra RUN adduser sinatra sudo RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers USER sinatra RUN curl -sSL https://get.rvm.io | bash -s stable RUN /bin/bash -l -c "source /home/sinatra/.rvm/scripts/rvm" RUN /bin/bash -l -c "rvm install 2.1.2" RUN /bin/bash -l -c "gem install sinatra" RUN /bin/bash -l -c "gem install thin"使用這個鏡像建立的容器可以便捷地被部署到一個主機上,這個主機同時還運行了另一個使用Java和DropWizard 定義的Docker鏡像所建立的容器。容器執行緩解隔離了主機上運行的不同容器,因此不存在使用不同語言、庫和框架容器所造成的沖突問題。
同時值得高興的是,近期發布的Amazon EC2 Container Service(Amazon ECS)可以幫你搞定所有這些工作。使用Amazon ECS,你可以定義一個被稱為“cluster”的計算資源池,一個cluster由一個或以上的EC2實例組成。Amazon ECS負責管理集群中所有基于容器的應用程序,提供 telemetry和logging,并管理集群的容量優化,進行高效的任務調度。Amazon ECS提供了一個“任務內容(task definition)”的理念,它可以定義組成一個應用程序的一組容器。task definition中的每個容器都指定了該容器所需的資源,而Amazon ECS將基于集群中的可用資源來調度這個任務的執行。
微服務可以非常便捷地被定義為一個任務,它可以由兩個容器組成——一個負責運行服務終端代碼,另一個負責運行數據庫。Amazon ECS可以管理這些容器之間的依賴性,同時也可以跨集群進行資源平衡。同時,Amazon ECS還可以無縫的訪問多個AWS重點服務,比如Elastic Load Balancing、Amazon EBS、Elastic Network Interface和Auto Scaling。通過Amazon ECS,使用 Amazon EC2部署應用程序的所有基本特征都對基于容器的應用程序可用。
此外,類似Amazon ECS 這樣的容器解決方案還可以簡化“service discovery(服務搜尋)”這樣的實現。因為微服務往往會跨多個主機部署,并根據負載進行縮放,service discovery更有利于服務之間的定位。在最簡單的情況下,可以使用負載均衡器來進行,但是在更為復雜的環境中,一個真正的分布式配置服務非常有必要,比如Apache Zookeeper。使用Amazon ECS API,與類似Zookeeper這樣的第三方工具整合將非常容易。配置了Zookeeper的容器可以被添加到一個task definition中,并可以通過Amazon ECS在集群中的Amazon EC2調度執行。
從許多方面來看,使用容器技術實施微服務架構轉變都與過去20年Web開發的衍變非常類似。許多這些衍變都是為了更好的利用計算資源,以及更方便的維護越來越復雜的Web應用程序。如我們所見,使用Linux容器技術來實現微服務架構完全匹配了這兩個需求。在本文中,我們簡單地接觸了使用Amazon ECS來定義一個微服務架構,但是容器在分布式系統中的使用已經遠超過了微服務。在分布式系統中,越來越多的容器成為了first class citizens,而在未來的報告中,我將討論為什么 Amazon ECS對管理給予容器的計算是至關重要的。
相關參考文章:一個更好的開發/測試體驗:在AWS上運行Docker
總結
以上是生活随笔為你收集整理的微服务架构(Microservices)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法】递归|迷宫回溯问题|八皇后问题
- 下一篇: 小米手机 加载桌面 失败 黑屏