Microservice架構模式簡介

  在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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章