在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同僚,出版了一本新書《Building Microservices》。該書描述了如何按照Microservice架構模式設計及搭建一個具有良好擴充性并可持續開發的系統。除此之外,該書還将基于該模式的系統演化流程與Continuous Delivery等目前甚為流行的開發流程結合在了一起,使得Microservice架構模式看起來非常具有吸引力。基于這些原因,該架構模式迅速被業界所熟知,并在多個産品中被嘗試着使用。這其中就包含了我們公司的産品vRA。
在這一年多的時間裡,我們不但真正地體會到了Microservice所具有的一系列優點,也犯過一系列錯誤。是以在這篇文章裡,我會對Microservice架構模式進行簡單地介紹,并将我們所得到的經驗和教訓介紹給大家。
Monolith
網上對Microservice進行介紹的文章常常以Monolith作為開頭,我也不會例外。原因是,知道了Monolith的不便之後才能更容易地了解Microservice架構模式所具有的各種優點。
首先請回想一下我們所開發的服務是什麼樣子的。通常情況下,這個服務所對應的代碼由多個項目所組成,各個項目會根據自身所提供功能的不同具有一個明确的邊界。在編譯時,這些項目将被打包成為一個個JAR包,并最終合并在一起形成一個WAR包。接下來,我們需要将該WAR包上傳到Web容器中,解壓該WAR包,并重新啟動伺服器。在執行完這一系列操作之後,我們對服務的編譯及部署就已經完成了:

這種将所有的代碼及功能都包含在一個WAR包中的項目組織方式被稱為Monolith。在項目較小的情況下,這種代碼組織方式還是可以接受的:更改完代碼後,軟體開發人員可以趁着編譯器編譯代碼的時候沖杯咖啡,并在回到座位後花費一分鐘部署剛剛編譯出來的WAR包以便測試自己剛剛所做的更改。但随着項目的逐漸變大,整個開發流程的時間也會變得很長:即使在僅僅更改了一行代碼的情況下,軟體開發人員需要花費幾十分鐘甚至超過一個小時的時間對所有代碼進行編譯,并接下來花費大量的時間重新部署剛剛生成的産品,以驗證自己的更改是否正确。
如果應用的部署非常麻煩,那麼為了對自己的更改進行測試,軟體開發人員還需要在部署前進行大量的環境設定,進而使得軟體開發人員的工作變得繁雜而無趣:
從上面的示意圖中可以看到,在應用變大之後,軟體開發人員花在編譯及部署的時間明顯增多,甚至超過了他對代碼進行更改并測試的時間,效率已經變得十分低下。
在變得越來越大的同時,我們的應用所使用的技術也會變得越來越多。這些技術有些是不相容的,就比如在一個項目中大範圍地混合使用C++和Java幾乎是不可能的事情。在這種情況下,我們就需要抛棄對某些不相容技術的使用,而選擇一種不是那麼适合的技術來實作特定的功能。
除此之外,由于按照Monolith組織的代碼将隻産生一個包含了所有功能的WAR包,是以在對服務的容量進行擴充的時候,我們隻能選擇重複地部署這些WAR包來擴充服務能力,而不是僅僅擴充出現系統瓶頸的組成:
但是這種擴充方式極大地浪費了資源。就以上圖所展示的情況為例:在一個服務中,某個組成的負載已經達到了90%,也就是到了不得不對服務能力進行擴容的時候了。而同一服務的其它三個組成的負載還沒有到其處理能力的20%。由于Monolith服務中的各個組成是打包在同一個WAR包中的,是以通過添加一個額外的服務執行個體雖然可以将需要擴容的組成的負載降低到了45%,但是也使得其它各組成的使用率更為低下。
可以說,所有的不便都是由于Monolith服務中一個WAR包包含了該服務的所有功能所導緻的。而解決該問題的方法就是Microservice架構模式。
Microservice架構模式
簡單地說,Microservice架構模式就是将整個Web應用組織為一系列小的Web服務。這些小的Web服務可以獨立地編譯及部署,并通過各自暴露的API接口互相通訊。它們彼此互相協作,作為一個整體為使用者提供功能,卻可以獨立地進行擴容。就以下圖所示的WikiPedia服務架構為例:
從上圖中可以看到,WikiPedia包含了一系列服務,如資料通路服務Databases,搜尋服務Search等。這些服務都包含了數量不等的服務執行個體,以確定能在不同負載的情況下為使用者提供優質的服務。在使用者的請求到達時,它們将協同工作,一起完成對使用者請求的響應。
在使用Microservice架構模式的情況下,軟體開發人員可以通過編譯并重新部署單個子服務的方式來驗證自己的更改,而不再需要重新編譯整個應用,進而節省了大量的時間。同時由于每個子服務是獨立的,是以各個服務内部可以自行決定最為合适的實作技術,使得這些子服務的開發變得更為容易。最後如果目前系統的容量不夠了,那麼我們隻需要找到成為系統瓶頸的子服務,并擴充該子服務的容量即可:
Microservice經驗談
以上就是對Miscroservice架構模式的介紹,是不是很簡單?實際上,這是一個正在發展的架構模式。在衆多讨論中,關于該模式的标準實作,以及最佳實踐等衆多話題并沒有完全達成一緻。是以我在這裡介紹的,是各個論壇讨論中基本達成一緻意見的一系列經驗。而各位在實作自己的Microservice架構模式時,一方面可以借鑒這些經驗,另一方面也可以根據項目本身需求調整Microservice架構模式的實作方法。
轉變你的視角
無論是在編寫一個服務,還是在編寫一個桌面應用,我們常常會首先嘗試将需要實作的功能分割為一系列元件,并圍繞着這些元件設計完成業務邏輯所需要的工作流及資料流。這種設計方法将導緻實作業務邏輯的所有元件都運作在同一個程序之内,并且各個業務邏輯的實作也在同一個程序之内運作:
但是在Microservice架構模式中,我們需要更高一層的分割:在嘗試将需要實作的功能分割成為一系列元件之前,我們首先需要考慮如何将需要實作的功能交由彼此互相獨立的一系列服務來完成。例如在一個電子商務網站中,對使用者購買商品這一業務流程的支援就可以交由三個服務來完成:在使用者浏覽商品的時候,其使用的是商品浏覽服務;在使用者将商品添加到購物車并生成訂單的時候,其使用的是訂單服務;而在使用者進行網上支付的時候,其使用的則是付款服務。根據這種分割思路,我們的應用将運作在三個獨立的程序之中:
同時這三種服務各自的側重點并不相同:商品浏覽服務中,對資料庫的讀操作比寫操作多得多,是以對讀操作進行優化将非常顯著地提高服務的運作性能;而訂單服務則是寫操作居多,是以我們需要對訂單的寫入性能進行優化;付款服務涉及到使用者的财産,是以其對安全要求會偏高一些。這種差異可能導緻最适合實作這三個服務的技術各不相同。由于這些服務是完全獨立的,是以我們完全可以根據子服務的需求來決定所需要使用的技術,而不再需要考慮這些類庫是否與已有系統相容。
使用最合适的技術所帶來的優點就是,服務的代碼會變得非常清晰明了,甚至在有些情況下可以達到簡潔優雅的程度。在一些讨論中,有些人甚至建議一個服務隻需要10到100行代碼(他們常用簡寫LoC,即Lines of Code)。再加上服務已經獨立出來,而不再與其它服務混合在一起,是以正确地使用Microservice架構模式大大提高了代碼的維護性以及新人上手的速度,也有助于技術人員在日常工作中進行技術集的更新及轉換。
但是這種對于服務的分割群組件之間的分割并不相同。最重要的一點就是在各個服務之間進行通訊的消耗相對于在同一個程序中而言是非常大的。在設計一個元件的時候,我們需要考慮該元件所給出的接口能夠盡可能地滿足目前及今後的一系列可以預見的需求,這便要求該元件所提供的API具有一定的前向相容性,并擁有一系列其它特性,如靈活性,擴充性等等。這常常導緻該元件所提供的API具有較細的粒度。在程式運作時,對該元件所提供的API的調用就在目前程序中進行,速度非常快,是以頻繁地對該細粒度API進行調用并沒有太大的問題。但是一個跨服務調用所需要的時間則比程序内調用的時間長很多。如果在處理一個請求的時候需要太多的跨服務調用,那麼整個應用的性能将變得無法忍受。是以我們在執行服務分割時定義的API需要是粗粒度的API。
就讓我們以一個電子商務網站為例。在為使用者生成訂單時,電子商務網站常常需要列出各個商品的主要資訊,商品的價格,優惠幅度,并通過庫存系統檢驗該商品的庫存,進而得到整個訂單的内容。如果每次與其它服務溝通都需要100毫秒,而且整個訂單包含了20件貨物,那麼系統準備訂單的時間就會達到8秒(100ms * 4次調用 * 20件商品)。這從使用者的角度來說是不可以接受的性能。而且随着訂單中所包含商品數量的增多,系統準備訂單的時間會線性增長,進而使得系統的性能更加不可忍受。
究其原因,實際上還是因為準備訂單所調用的API的粒度太細了。如果訂單系統能夠一次性地把一件商品的主要資訊,價格,優惠幅度以及庫存資訊從商品服務中取回來,那麼其效率就将提高四倍。如果訂單系統不需要為每件商品依次發送請求,而是可以通過一次性地服務間調用就能取回所有需要的資訊,那麼系統準備訂單的時間将不會再随着訂單的增大而增長。是以在Microservice架構模式中,各個服務應該提供可以被靈活使用的粗粒度API,以減少各種跨服務調用的消耗。
除了各個服務所提供的API的粒度,服務分割的粒度也是在服務分割過程中需要考慮的因素。如果一個服務的粒度太小,那麼它所提供的API的粒度也不會高。一個較為普遍的看法是,在Microservice架構模式中,一個服務需要能夠獨立地完成特定的業務邏輯,至少是某個獨立資源的CRUD操作。例如在電子商務網站中,我們需要一個服務能夠獨立地完成對商品相關資訊的讀取,如商品的主要資訊,商品的價格,參與的優惠活動等。
這裡有一個例外,那就是公共功能的處理。試想在一個應用中,我們常常需要一個權限管理元件來管理使用者所具有的各個權限。權限管理元件常常實作了一種公用的安全模型(Security Model),如ACL(Access control list),RBAC(Role-based access control)等。在每次通路一個子服務的時候,這些服務都需要檢查使用者所具有的權限:
發現問題了麼?是的,每次對一個産品系統及訂單系統的調用都需要從權限系統中得到目前使用者的權限,才能決定使用者是否能夠通路特定資訊。如果這樣的公共服務很多,那麼該系統的性能将會變得非常差。
解決該問題的一種方法就是在各個系統中将下次還能夠使用的資訊緩存起來,也就是在這些系統中為使用者建立一個會話。由于每個系統可能由多個服務執行個體所組成,為了能夠重複利用會話中所儲存的資訊,減少向公共服務發送請求的次數,我們需要通過負載平衡技術讓系統中的同一個服務執行個體處理同一個使用者的請求。有關如何實作該功能,請見我的另一篇文章《企業級負載平衡簡介》。
除了性能問題之外,公共服務還會與各個服務産生一種邏輯上的依賴關系。讓我們繼續通過權限系統這個例子進行讨論。當權限管理的組成存在于各個服務中的時候,我們可以直接通過傳入使用者的資訊以及需要通路的資源就能判斷出到底使用者是否能夠通路特定資源。也就是說,從權限管理組成所傳回的實際上就是一個布爾類型的資料。但如果權限管理不再是一個組成,而是一個服務,那麼為了避免每次都調用權限管理服務,我們需要在使用者的會話中記錄使用者所具有的權限。在使用者下次通路該服務的時候,我們可以通過直接檢查該使用者所具有的所有權限就能決定其是否能夠通路特定資源了。這些在使用者會話中記錄的權限常常具有其特定的表現方式,例如特定形式的字元串,而這種字元串表示需要同時被服務和權限管理服務所了解,進而造成了這兩個服務之間的耦合:
但是這種方式為子服務增強了對權限系統的依賴性。群組件之間的耦合一樣,增大的耦合性會導緻服務的重用性下降。
是以說,如何對服務進行分割實際上是Microservice架構模式中最需要技巧的事情。在分割過程中,服務的總體性能是至關重要的,而各個服務的獨立性也是大家所最為關心的特性。當然,Microservice架構模式仍在逐漸發展中,是以相信會有越來越多的實踐經驗被大家所發掘出來,進而指導我們更好地對服務進行分割。
共享服務
在前面一節中,我們已經提到了公共服務。實際上,這是Microservice架構模式中最需要技巧的一部分。
實際上,Microservice架構模式實作中常常需要一系列公有服務以輔助整個應用的運作。除了我們剛剛提到的權限管理服務,我們還需要能夠監控各個服務執行個體的服務狀态,服務執行個體的添加删除更新管理等等。這些服務在各個子服務的服務執行個體之間共享,甚至可以在其它應用中被重用。
隻是很多人擁有一個這樣的誤區,那就是Microservice架構模式可以讓服務的開發變得更容易。而實際情況則恰好相反。在剛開始使用Microservice架構模式開發應用的時候,其效率是明顯低于通過Monolith進行開發的:
從上圖中可以看到,在剛開始的階段,使用Microservice架構模式開發應用的效率明顯低于Monolith。但是随着應用規模的增大,基于Microservice架構模式的開發效率将明顯上升,而基于Monolith模式開發的效率将逐漸下降。
為什麼呢?這是因為Microservice是一個架構模式,而不是一個特定的技術解決方案。其并不會将開發中的各個難點全部轉移,而隻是允許通過更為合适的技術來适當簡化單個子服務的開發,或者繞過開發中可能遇到的部分難點。但是為了支援各個子服務的運作,我們還需要建立一系列公共服務。這些公共服務需要在編寫第一個子服務的同時進行。這是導緻Microservice架構模式在開發初期會具有較低效率的一個原因。
然而使用特定技術并不會繞過開發中所能遇到的所有難點。由于在Microservice架構中,各個子服務都集中精力處理本身的業務邏輯,而所有的公共功能都交由公共服務來完成,是以公共服務在保持和各個子服務的松耦合性的同時還需要提供一個足夠通用的,能夠在一定程度上滿足所有目前和未來子服務要求的解決方案。而這也是導緻Microservice架構模式在開發初期會具有較低效率的另外一個原因。
而在開發的後期,随着Monolith模式中應用的功能逐漸變大,增加一個新的功能會影響到該應用中的很多地方,是以其開發效率會越來越差。反過來,由于Microservice架構模式中的各個子服務所依賴的公共服務已經完成,而且子服務本身可以選擇适合自己的實作技術,是以子服務的實作通常隻需要關注自身的業務邏輯即可。這也是Microservice架構模式在後期具有較高效率的原因。
當我們再次通過Microservice架構模式搭建應用的時候,其在開發時的效率劣勢也将消失,原因就是因為在前一次基于Microservice架構模式開發的時候,我們已經建立過一次公共服務,是以在這個新的應用中,我們将這些公共服務拿來并稍事改動即可:
從上圖中可以看到,雖然我們仍然需要花一些時間來對公共服務進行一些修改,但是此時所導緻的效率下降已經不再那麼明顯了。也就是說,就算是在前期,我們已經擁有了較高的開發效率。
而且随着Microservice架構模式的不斷流行,在網絡上會有越來越多的使用者共享自己的公共服務解決方案。那麼第一次按照Microservice架構模式編寫應用所導緻的性能下降也會逐漸變得越來越小。
模型比對
OK。在介紹了共享服務之後,我們就可以讨論Microservice架構模式中的另外一個問題:模型比對了。在Microservice中,各個服務是彼此獨立的,而且是關注于自身業務邏輯的。是以在看待一個事物的時候,Microservice可能擁有不同的視角,進而造成了各個子服務中的對應模型并不比對。
例如在一個IaaS雲中,一個使用者所具有的角色可能會根據他所擁有的職責來劃分:雲上擁有一系列用于監控的使用者,用來完成對雲的整體運作監控等工作(不包含檢視使用者資料,這是個安全問題)。同時雲上的使用者又可以分為帳号管理者,Tenant管理者,資源管理者以及普通使用者等。而在其上運作的應用即服務(AaaS,Application as a Service)中,其使用者的職責劃分可能是另一個樣子:在AaaS上定義應用的是應用架構師,負責應用部署及維護的則是運維人員。在應用架構師設計一個應用的時候,其并需要擁有IaaS雲上通路資源的權限,卻并不需要配置設定資源的權限,但是運維人員需要擁有該權限以對應用進行部署和維護。
也就是說,IaaS雲中的權限劃定和AaaS服務中的權限劃定并不一樣。通常情況下,我們常常在業務內建時執行一次對權限的比對:
從上圖中可以看出,由于AaaS服務是運作在IaaS之上的,是以為了能夠操作IaaS中所包含的各個資源,AaaS服務需要将自己的使用者角色比對到IaaS所定義的角色上。例如應用架構師需要能夠在定義應用的時候需要知道IaaS上所具有的資源,并需要能夠指定到底哪些人可以使用這些應用,是以其需要擁有IaaS的Tenant管理者,資源管理者及普通使用者三種角色。而AaaS上的運維人員則隻需要在部署和維護時察看IaaS上所擁有的資源,是以其隻需要資源管理者及普通使用者兩種角色。
但是這麼做有兩點不好的地方:如果Microservice中隻包含了幾個服務,而且這種服務之間的依賴關系并不是很多,那麼這種服務比對還能夠解決,但是如果整個系統之間各個子服務的溝通很多,那麼在各個子服務之間進行角色比對将變成一個噩夢:
解決該問題的方法就是使用我們上節所介紹的公共服務對它們進行管理。在提供一個集中的公共服務的情況下,我們就不再需要處理這麼多的模型轉化了:
除此之外,僅僅簡單地對角色進行比對實際上并不那麼合适:就應用架構師而言,其需要的是檢視目前的已有資源,卻不需要對資源進行配置設定。是以其需要的是對資源的讀權限。而運維人員則不僅僅需要能夠讀取資源資訊,更需要對資源進行配置設定,是以其需要的是資源的讀寫權限。如果僅僅像上面那樣在IaaS層為應用架構師賦予對資源的讀寫權限,那麼應用架構師就可能擁有了錯誤的權限,進而執行了錯誤的操作。而相對地較為合适的方式則是對這些權限進行細分,即在權限中區分讀寫權限等:
是以在集中的公共服務中,我們需要使用較為細粒度的模型。該細粒度模型需要具有較高的靈活性,以能夠無損地表示各個服務中的相應模型。
相信您現在已經能夠看出,雖然說Microservice架構模式将單個子服務的實作簡化了,但是複雜化了資料的處理。是以相較于我們以往所編寫的應用,Microservice架構模式會在資料相關的一些特性上遇到一系列麻煩。
一個較為常見的麻煩就是保持多個子服務之間資料的一緻性。我們知道,在服務中,保持一緻性的工作常常是由事務來完成的。而如果希望在Microservice架構模式實作中保持子服務之間資料的一緻性,我們可能就需要使用分布式事務了。但是分布式事務本身就是一個非常複雜并且難以操作的東西,是以就現在而言,這種問題實際上是非常難以解決的。但是反過來講,事務本身也是表示一種邏輯上的強耦合,是以我們需要真正反思的則是這些需要使用事務來保持資料一緻性的子服務是否應該屬于同一個服務。當然,我們可以在某種程度上借鑒NoSQL資料庫中的一些做法。例如在一個服務更新了資料以後,我們使用一種異步機制來保持資料的一緻性,就好像很多NoSQL資料庫不保證使用者的資料立即可讀一樣。
另一個較為常見的麻煩就是粒度的問題。我們在前面已經說過,在Microservice的各個子服務之間進行服務間調用效率是十分低下的。為了減少多次服務間調用,各個子服務所提供的API的粒度需要盡量地粗,卻需要盡量地保持靈活性。最好的情況就是可以通過一次服務間調用來得到所有想要的資訊。
項目管理
除了上面所讨論的一系列技術因素之外,Microservice架構模式的開發還存在着一系列項目管理上的難題。
首先,由于Microservice架構模式中的各個子服務可能使用了不同的技術搭建,例如有些子服務是由Java開發的,有些則是由Python開發的,而且它們所使用的Servlet容器并不相同,是以由Microservice架構模式所搭建的應用可能需要非常複雜的環境設定。這對于傳統的運維人員來說是非常困難的一個任務。而相對于這些運維人員而言,負責各個子服務開發的開發人員才是有關該服務運作及部署的專家。是以在Microservice架構模式中,開發及運維的職責均發生了變化:開發人員不僅僅需要負責子服務代碼的編寫,還需要考慮該子服務的日常運維。而運維人員需要向開發人員給出一些運維相關的建議,并在總的方向上掌控産品的日常運維。
這樣做的好處則在于:開發人員會直接接觸到生産環境,可以快速地跟蹤并解決問題,而不再需要通過客戶及運維人員的轉述等步驟才開始處理問題,也避免了在轉述過程中出現的偏差。除此之外,開發人員也能更清楚地了解使用者到底是如何使用他們所建立出來的産品的,進而建立出來更容易被使用及管理的子服務。
但是這也會導緻項目管理出現一些困難。首先,不論是開發人員還是管理者都需要了解并處理一系列運維相關的問題。這會分散他們的注意力,使得開發效率的降低。其次,由于一個子服務常常同時包含前端,背景,資料庫,測試,甚至運維相關的一些任務,是以子服務的開發人員常常需要了解服務開發的大部分組成。這種人才在中國市場上并不多見,是以比較搶手。而且由于一個開發人員需要接觸太多的功能和技術,是以很多時候沒有辦法深入地研究它們。由此所導緻的問題則是,在遇到較為困難的問題時,軟體開發人員需要花費較多的時間來分析并解決該問題。如果該問題較為嚴重,那麼它将會嚴重影響整個組的開發進度。從項目管理的角度來講,這實際上是一件非常危險的事情。
一個理想的解決方案就是,目前子服務所使用的各個技術都有一個專家。但是一個全棧開發人員,還需要是某一方面的技術專家,雇傭該人的成本可想而知。
除此之外,我們還需要在按照Microservice架構模式開發的時候使用一系列标準化的開發及測試流程。其中和Microservice最自然契合的就是現在最為流行的Continuous Delivery,或被稱為是DevOps。在這些自動化流程的幫助下,軟體開發人員可以快速地完成一次疊代:在對代碼更改完畢以後,軟體開發人員可以直接開始對自己的更改進行編譯,運作單元測試及功能測試。接下來,系統将會把剛剛編譯好的代碼自動進行部署,并在整個系統中執行內建測試。在內建測試完畢之後,品質管理人員或軟體開發人員自己會在該系統中進行一次測試,并在完成測試後進行複雜的性能測試,并在通過性能測試後進行部署。
所有這一切實際上都和使用Monolith開發時所使用的流程類似。唯一不同的是,在基于Microservice架構模式的開發中,這種自動化的流程變得更為重要了。因為基于Microservice架構模式所搭建的應用常常使用了不同的邏輯,是以部署一個完整的環境就會變得非常複雜。是以由這些自動化流程來負責測試環境的部署則大大地減輕了軟體開發人員的負擔,也是提高軟體開發人員工作效率的基礎。
同時由于軟體開發人員需要随時執行應用程式的部署來測試自己剛剛所做的更改,是以其需要能夠随時配置設定到其所需要的各個資源,如部署應用所需要的計算資源,記憶體以及存儲等。而這種功能則正是雲這種商業模式所提供的功能。是以在開發基于Microservice架構模式的應用時,我們則盡量基于某些雲來開展我們的持續開發流程。
Microservice實作
在本節中,我們将對實作Microservice架構模式時所常用的一些方法進行講解。
相信大家的第一個問題就是,Microservice架構模式中各個子服務應該如何互相協作以向使用者提供服務的呢?按照上面我們的講解,Microservice架構模式中各個子服務應該是獨立的,否則它們之間将産生耦合,進而帶來一系列問題:這些子服務彼此不獨立,需要使用分布式事務保持其資料一緻性,子服務不易被重用等。但是如果這些子服務絕對獨立,甚至不包含一點點邏輯上的耦合,那麼它們之間也将無法進行協作。是以在論壇讨論中常常出現的問題就是,這些子服務之間哪裡可以出現耦合?可以出現什麼程度的耦合?
這個問題實際上非常簡單,那就是UI。我們知道,在一個BS服務中,服務端和用戶端之間存在着一定程度的耦合。兩者通過服務所暴露的API進行溝通。而基于Microservice架構模式的服務也不例外:
既然運作在使用者浏覽器中的UI需要與其它各個子服務進行互動,那麼它完全可以作為一個中介者來完成各個子服務之間的互動。例如在顯示産品頁面的時候,該頁面邏輯會向産品服務及庫存服務同時發送請求,以并行地得到産品的詳細資訊以及該産品的目前庫存。
是以在一個基于Microservice架構模式的服務中,常常會出現一個前端服務。該服務所提供的頁面會與各個服務溝通。但是它實際上與各個子服務之間卻不需要通訊:
或許您會說,在這種情況下,我們的各個子服務就沒有UI了。而UI服務不僅僅需要處理所有的前端業務邏輯,而且随着時間的推移,其可能會變成另外一個龐然大物。除此之外,如果希望整個平台能夠允許第三方服務接入,那麼這種打包在一起的UI服務将變成整個平台擴充性的阻礙。
是的。如果需要解決這個問題,那麼您就需要在應用中嘗試借鑒Service Locator模式。此時我們需要的則是一個UI架構,其允許使用者通過特定方式在應用中插入各個子服務所提供的UI,并允許您通過一些機制來發現已經在平台中注冊的具有特定功能的API,并允許您對該API進行調用。我相信,随着Microservice架構模式的不斷發展,會有越來越多的支援這種擴充方式的UI類庫出現。
另外一種模式則是Message Broker。簡單地說,Message Broker就是一個消息的中轉平台。該平台允許其它組成向其中注冊消息,也允許其它組成偵聽消息。當一個組成将一個消息發送到了Message Broker之上後,其它偵聽該消息的各個組成則會根據消息中所包含的資訊更新自己的狀态。
反過來,如果您的服務需要支援移動裝置,如手機,iPad等,我們就不能讓這些移動裝置一個一個地通路子服務了。這是因為這些移動裝置的帶寬一般來說都非常小,而且使用者常常處于信号不是很好的地方,是以在向這些子服務一個個地發送請求将快速消耗掉它們所擁有的有限的帶寬。為了解決這個問題,我們常常需要在這些子服務前搭建一個代理服務。該代理服務會将使用者請求根據業務邏輯拆分為對各個子服務的請求,并将各個子服務所傳回的結果歸納為一個響應傳回給使用者:
當然,上面所介紹的僅僅是目前論壇讨論中所常常提到的一種搭建基于Microservice架構模式應用的方式。或許在不久的将來,您會看到設計得越來越精巧的各種模式出現。
在講解完這些子服務該如何展現給使用者之後,我們就來講解一下如何建立各個子服務所需要的公共服務。之前我們已經提到過,由于對公共服務的調用是一個跨程序調用,是以其相較于程序内調用效率非常低下。在這種情況下,我們需要盡量避免對該公共服務的重複調用。為了達到該目标,我們需要盡量使使用者通路同一個子服務執行個體,并且在該使用者的會話中緩存從公共服務中所得到的資訊。
是以在同一個子服務的各個服務執行個體上,我們需要盡量使用負載平衡服務的Sticky Session的功能,并在一次公共服務調用中取得多項資訊。例如在檢視使用者的權限時,我們不是傳回使用者是否具有特定權限,而是該使用者擁有哪些權限。當然,這不僅僅需要從Microservice這種架構模式的方面來考慮,還需要同時兼顧安全,維護性等一系列問題。
簡單地說,在兼顧其它方面的情況下,我們需要将公共服務API的粒度定得粗一些,同時也需要具有一定的靈活性,進而通過減少服務間調用來避免整個服務的性能瓶頸。
既然說到了API的粒度,那我們就需要讨論一下各個子服務所提供的API了。和公共服務一樣,各個子服務所暴露的API也應該具有較粗的粒度以及較大的靈活性。除此之外,我們還需要讓這些子服務所暴露的API具有盡量一緻的樣式,如定義一系列RESTful的API。在這種情況下,與這些服務進行互動的組成,如網頁的UI,才能具有可以接受的維護性。
一個經驗性的觀點則是,Microservice架構模式中的“開”是各個服務的内部實作,而其中的“閉”則是各個服務之間互相溝通的方式。
如果您需要從頭開始搭建一個服務,那麼您需要首先考慮如何對這些服務進行劃分,并在建立第一個服務的時候開始搭建出各個公共服務的雛形,同時确定各個服務之間溝通所需要遵守的協定。當越來越多的子服務被建立出來之後,您需要逐漸豐富各個公共服務所提供的功能,使其逐漸變為功能強大的,可重用的服務。
如果您已經擁有一個Monolith服務,并且希望通過采用Microservice架構模式來緩解目前Monolith模式服務所具有的一系列問題,那麼您首先需要建立一個獨立的服務,并通過一個粘合層來與該Monolith服務互動。在該過程中,您可能需要将Monolith服務的内部接口逐漸暴露出來,以供這個新的服務使用。而這就是在抽象公共服務的過程。
接下來,您就需要根據上一步中所得到的接口來逐漸将Monolith服務中的公共服務剝離。在剝離過程中,您腦中需要記得的一句話還是:粗粒度,靈活的API。而其内部實作到底是什麼樣的,實際上并不會影響到您的剝離結果。
最後就是再從Monolith中剝離其它服務了。此時我們最需要考慮的就是在服務中具有鮮明特點的各個服務,如對資源的要求與整個Monolith服務格格不入,或者使用了和Monolith很難相容的技術等。
最後一種情況就是多個服務內建的情況。在産品的逐漸疊代過程中,我們常常會遇到需要将多個産品內建成為一個産品以提高整體競争力的情況。這常常發生在盈利産品和其它非盈利産品之間。而這正是實踐Microservice架構模式的絕佳機會。此時我們僅僅需要暴露一系列Monolith服務中的接口并建立粘合層即可。
Microservice的優點與劣勢
好,在前面我們已經講解了很多有關Microservice架構模式的經驗性方法和相關知識。那我們現在回顧一下Microservice所具有的一系列優點和劣勢,以使您能夠在采用Microservice架構模式之前全面地衡量該方案所可能得到的好處及遇到的困難。
首先,由于Microservice架構模式中的每個子服務都可以獨立于其它服務執行,是以其常常具有更好的服務邊界。而這個明确的服務邊界則會帶來一系列好處:在Microservice架構模式中,各個子服務執行所需要的業務邏輯都相對集中于子服務内。是以其實作代碼相對容易了解,并且便于維護。另外各個子服務所具有的結構,運作流程及資料模型都能夠更貼近于子服務所表示的業務邏輯,是以在代碼的開發速度和維護性上得到了大大地增強。同時各個子服務可以選擇最适合實作業務邏輯的技術,進而使得各個服務的開發變得更為容易。同時在出現新的更适合的技術時,我們可以較為容易地在各個子服務内部對原有的實作技術進行替換。
獨立性也意味着擴充性的增強。在Microservice架構模式中,各個子服務可以根據自身的負載獨立地進行擴容,如Scale Up或Scale Out等。不僅如此,我們還可以根據子服務自身的特性為其準備特定的硬體裝置,使得其運作在更适合的伺服器上。同時這種獨立性還可以使得各個子服務可以被重用。
同時這種獨立性也可以增加整個服務的容錯能力。例如如果一個子服務由于種種原因無法繼續提供服務,其它子服務仍然可以獨立地處理使用者的請求。
另外,各個子服務的獨立部署能力也可以大大地提高Continuous Delivery的運作效率。畢竟在這種情況下,軟體開發人員隻需要重新部署更改過的子服務就可以了。
由于Microservice架構模式中的各個子服務無論是在代碼量方面還是最終生成的WAR包方面都較Monolith架構所搭建的服務小,是以在IDE支援,啟動速度方面都具有相當的優勢。同時,這種小粒度的服務已經可以由一個幾個人所組成的小組來完成,而不再需要通過來自世界各地的不同小組協同開發,進而大大降低了溝通成本,提高了開發的效率。
但是反過來,Microservice架構模式中各個子服務的獨立性也會導緻一系列問題。最明顯的就是需要多個子服務互相配合的情況。由于這些子服務是不同的程序,是以在這些程序之間保持資料的一緻性,或添加一個新的跨子服務的使用者用例實際上都是一件非常麻煩的事情。而且對這些獨立服務在整個系統中是否能夠工作的測試需要運作大量的內建測試。而如果需要快速地對這些子服務進行開發和疊代,那麼我們就需要每個開發人員都能夠專業并高效地使用一系列自動化工具。這實際上也是一個不低的要求。
除此之外,基于性能考慮,各個子服務所提供的接口将是粗粒度的,卻具有較高靈活性的API。但是這種API擁有一個較明顯的缺陷,那就是越靈活的API,其使用起來的難度就越大。是以對于服務的使用者而言,其上手的難度則相對增加了。
另外,如何規範化各個子服務之間的溝通協定也是一個非常具有挑戰性的事情。因為在Microservice架構模式中,我們常常需要建立一系列公共服務。這些公共服務常常暴露特定樣式的接口以供其它服務調用。是以我們需要在這些接口上保持一緻性,進而才能夠更自然地編寫各個子服務的内部邏輯并暴露适當的接口。但是反過來,一緻的接口樣式常常會導緻各個服務的自然實作需要向這些标準進行妥協。是以我們常常需要在兩者之間平衡。
這些平衡方法包括标準化各個服務所暴露的接口,使用固定的幾種方式對子服務進行內建,保持資料模型格式的一緻性等。這些實際上都是我們自由編寫各個子服務的障礙。對此采取多麼嚴格的規範實際上是需要通過經驗累積來完成的,是以這大大提高了使用Microservice架構模式失敗的機率。
轉載請注明原文位址并标明轉載:http://www.cnblogs.com/loveis715/p/4644266.html
商業轉載請事先與我聯系:[email protected]