在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新書《Building Microservices》。該書描述瞭如何按照Microservice架構模式設計及搭建一個具備良好擴展性並可持續開發的系統。除此以外,該書還將基於該模式的系統演化流程與Continuous Delivery等當前甚爲流行的開發流程結合在了一塊兒,使得Microservice架構模式看起來很是具備吸引力。基於這些緣由,該架構模式迅速被業界所熟知,並在多個產品中被嘗試着使用。這其中就包含了咱們公司的產品vRA。html
在這一年多的時間裏,咱們不但真正地體會到了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
商業轉載請事先與我聯繫:silverfox715@sina.com