Microservices流言大解密 ?
Microservices(微服務)這個詞,相信大家多少都有聽過,但它代表什麼含義?有人說Domain Driven Design (DDD)就是Microservices,有人說使用Docker執行應用程式就是Microservices,有人說Service Oriented Architecture (SOA)就是Microservices。
眾說紛紜到底哪個是對的?希望本篇文章,能夠幫大家解決以下困惑:
- Microservices的定義、優缺點、使用時機
- 真Microservices vs 假Microservices
- SOA與Microservices之間的關係
- DDD與Microservices之間的關係
- Docker與Microservices之間的關係
Microservice的定義、優缺點、使用時機
在介紹Microservices之前,我們要先知道為什麼有Microservices的出現。
傳統開發應用程式的方式,我們會使用OOAD等技術建立一個Domain Model (希望你公司有,沒有的話請趕快逃),而整個應用程式圍繞此Domain Model進行開發,在OOAD被發明的年代,不需要太多的Scale、複雜度相較於現在也小非常多,進入到巨量資料時代後,隨著使用者急速暴增、應用程式的複雜指數增加,光靠一個Domain Model開發會導致以下問題:
- 僅透過單一Domain Model抽象化非常複雜的應用程式是不夠的,借用韓導的一句話:我談的是大海(複雜的應用程式),你跟我講漱口杯(單一Domain Model)。
- 使用者個急速暴增,我們需要High Transaction應用程式來處理大量的Request,但在複雜的應用程式內,不同區塊需要Scale的程度不同,如果使用單一Domain Model無法達到個別優化的效果(Independent Optimization)。
- 單一Domain Model雖然部署簡單,但即便是微小更新,也需要重新部署,專案Build的時間也會非常長,導致生產力下降。
- 複雜應用程式通常會有多個Team負責不同的區塊,由於OOAD過度強調抽象化整個應用程式,會導致Team與Team之間互相影響,舉個例子:Order物件會有物流的責任、財務的責任、餐廳訂單的責任,明顯違反ISP,既便Team僅負責物流的部分,也會受到財務、餐廳訂單的影響。
Microservices comes to rescue
因為認知到上述的問題,才會有Microservices的出現,Microservices針對應用程式不同的商業領域切割成多個Services,每個Service負責單一的商業領域,像是Order Management Service、Customer Management Service等等。
Why Microservices:如果正確使用Microservices,應用程式會由A Set of Loose Couple Services所組成,就能解決上述只用OOAD塑模整個複雜應用程式的問題。
Microservices優點:
- 因為應用程式是由A Set of Loose Couple Services所組成,每個Service只負責單一的商業領域,所以可以大幅度降低應用程式複雜度。
- Loose Couple Services的特性,也讓Team與Team之間溝通的Overhead大幅度降低,相較於傳統的OOAD,Team與Team之間會在同一個Codebase互相衝突。
- Service可以獨立部署,不會影響其他Teasm,相較於傳統OOAD,我們需要重新部署整個應用程式。
- 針對單一個Service撰寫測試會比較容易,相較於傳統的OOAD,撰寫測試非常困難,因為一個物件可能牽扯多個Team的功能,導致測試非常Fragile (Multiple Reason to Fail)。
Microservices缺點:
- 如何切割Service是一門藝術,如果切得不好,會導致Distribute Monolithic,沒有享受到Microservices帶來的優勢 (Loose Couple Services),修改程式碼從原本單一Codebase轉變成跨Process的Service。
- 複雜度高,需要軟體團隊有良好的開發技術、高度自動化部屬環境,台灣的環境可能連傳統的OOAD都做不好 :( 。
- 維持多個Services之間的Data Consistency比傳統OOAD的單一Domain Model更困難,需要使用Saga Pattern。
- 因為Usecase可能由多個Services完成,相較於傳統OOAD,要在多個Services Trace Log並且除錯,會比較困難。
- 相較於傳統OOAD,針對Services進行測試的撰寫較為困難。
When Microservices:當使用OOAD開發應用程式時,遇到一開始介紹的的問題時,強烈建議可以慢慢把Monolithic轉變成Microservices。
Microservices真真假假
市面上不乏許多公司聲稱自己的架構是採用Microservices,那我們要如何判斷是否為真?
- Database Isolation Level:Microservices的應用程式是由A Set of Loose Couple Services組合而成,而Loose Couple的首要條件就是封裝,當Service之間如果會互相存取對方的Database Table,代表Service之間耦合性過高,會導致Distribute Monolithic。把Database想像成Class的Private Field,我們不會直接存取物件的Private Field (希望你能認同這句話),因此也不會存取Service的Database Table。
- Service Per Process:Service之間我們希望可以達到完全的Isolation,因此通常採用IPC (Interprocess Communication)的方式溝通,如果單純用Function Call就不是Microservices,因為Function Call會導致Service之間的耦合過高,也無法達到Microservices所說的Fault Tolerance。
- Team Organization:多個Team負責一個Service,代表Service切得太大,導致Team之間有溝通的Overhead,降低Microservices的優點:Team與Team之間能夠獨立運作。
- Deployment: Service可以單獨部署、單獨Scale,如果說部署一個Service時需要連帶其他Service一起部署,代表Service之間的耦合性太高,等同於Distribute Monolithic。
上面幾個條件看起來很簡單,實際上實作時卻非常困難,公司需要有對的Process (Scrum, XP)、自動化部屬環境 (DevOps)、Organization (Cross Functional Team)、自動化測試 (Unit Test, Integration Test),否則使用Microservices只是自討苦吃,搞死底下的工程師。
這邊附上一句名言:
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.
— Melvin E. Conway
Microservices與SOA之間的關係
兩者之間最大的差異在於:
Isolation Level:
- SOA:相較於Microservices,沒有特別強調連Database Table都要Isolate。
- Microservices:Database Table都要Isolate,溝通只能透過Public API。
Protocol:
- SOA:使用Heavy Weight Protocol,像是SOAP、WS*。
- Microservices:使用Light Weight Protocol,像是RabbitMq、RESTful。
Granularity:
- SOA:中等
- Microservices:較小
DDD與Microservices之間的關係
基本上兩著沒什麼關係,DDD的核心釐清Bounded Context,並且於Bounded Context內使用Ubiquitous Language開發Domain Model,也就是Ubiquitous Language in Code。
DDD與傳統OOAD不同的是,DDD認清到不可能使用單一Domain Model去針對過度複雜的應用程式建模,因此切割多個Bounded Context,在Bounded Context內建立屬於此Context的Domain Model,跳脫Bounded Context外,此Domain Model就沒有意義了,這樣的方式可以大幅度降低系統複雜度。
上面我們提到切割Service對於Microservices來說是非常重要的,剛好DDD的Bounded Context能幫助我們去判斷如何切割Microservices。
因此使用DDD不代表一定要用Microservice,而是我們可以藉由DDD的方式來切割Microservice的架構,DDD的Bounded Context也沒有規定溝通一定要用IPC,單純Function Call也可以。
Docker與Microservices之間的關係
傳統開發部署我們可能會使用VM的方式進行部署,因為只有一個應用程式,舉Java為例子,我們只需要一個Jar便能夠進行部署,因此使用VM非常方便。
但在Microservices的Context底下會產生一些問題,
- Microservices希望Service之間可以達到完全的隔離,因此如果用Service Per VM的方式去部署,會浪費過多資源 (因為VM需要透過Hardware Virtualization的方式去模擬OS,會吃掉過多資源)。
相較於VM,Docker直接與Linux Kernel溝通,效能比VM更好,也因為有Limit Access Control、Linux Namespace的關係,Container之間可以達到Isolation的效果,所以使用Docker的方式來部署Microservices能夠降低效能的浪費,也比較有效率。
所以我們可以說,因為Microservices的關係,我們需要切割成多個Service,使用VM進行部署,會導致資源浪費,因此Docker可以達到Good Resource Utilization的效果。
但是並不代表光使用Docker包覆一個Monolithic App就是Microservices。
簡單來說,Docker是為了讓Microservices的部署更加方便,更好管理Service。不會因為使用Docker程式就自動變成Microservices。
結論:先有因才有果,如果只看果,永遠看不到因,不要陷入Framework Trap,了解各個工具的Trade Off才能不被工具使用,是你使用工具,不是工具使用你。