以人來說,溝通很重要,而微服務也是。
對開發者而言,「服務(Service)」應該是一個能夠被單獨布署且能夠自主提供某些功能的應用程式。通常,我們會提供某種「介面(Interface)」使外部的使用者或需求單位能夠存取服務的內容。這個介面可能是使用者介面(User Interface)、命令列介面(Command-line interface),或者是基於 HTTP 的應用程式介面(Application Programming Interface)。對於微服務而言,介面的選擇將是服務間溝通的一大重點。
當有相同目標且不同職能的服務聚集在一塊時,我們得開始思考如何讓這些服務能有效地溝通。不可避免的,分散式系統架構將提高開發的複雜性,以微服務開發的應用程式將會產生大量的小型服務,而不同服務間的整合以及溝通模式的選擇將是一項開發上重要的決定。目前,多數的現有服務端點在 API 資源設計上,多採用基於 HTTP 協定的 RESTful 的設計方案。
已經有相當多的文獻探討在微服務中可行的技術與架構,但在這個系列的文章中我們將會集中地討論基於 HTTP 的溝通模式。在此所需要注意的是,於軟體技術百花齊放的今天,應用程式溝通模式並該不侷限於某種技術。
同時,Richardson 在其著作[1]中提到,在實作微服務架構時必須解決一些已知的缺點,開發人員必須面對建構相比單體式架構更加複雜的分散式系統而產生的額外問題:
在這個系列文章中,我們將會著重於上述的問題中進行探討。
客戶端直接呼叫服務
有一個常見的應用程式設計方式為「客戶端直接呼叫服務」,通常前端應用程式會透過單次或多次的連線,與後端服務溝通取得、修改、刪除以及檢索資料。
在微服務的架構中,一個完整的應用程式服務可能是由數個後端小型服務提供的。在這種情況下,若是讓客戶端直接呼叫這些分散的小型服務,可能會造成一些問題:
由客戶端直接存取分散的後端服務是不切實際的,若分散的後端服務在沒有被封裝的情形下直接接受呼叫,應用程式開發人員將會難以開發及維護應用程式。因此,我們應該尋求更有效的架構來因應可能產生的問題。
閘道器(Gateway)模式
我們能夠利用 API 閘道器模式解決上述問題。在閘道器模式中,所有客戶端的連線請求將統一透過閘道器的處理,再分發至正確的服務終端。
透過實作閘道器模式,將能夠解決微服務與客戶端過度耦合的問題。客戶端只需記錄閘道器的地址,即可存取所有小型服務提供 的 API。同時,API 閘道器也能夠同時負起身分驗證與流量限制的功能,也能夠將客戶端難以呼叫的協定,透過閘道器作為中介層包 裝成較為友善的 HTTP 協定。
API 組合(Composition)模式
當客戶端的業務邏輯需要同時對多個 API 進行查詢時,可能會發生對伺服器進行大量請求的情形。客戶端可以透過單一請求要求閘道器執行查詢,閘道器將會從多個服務提供者中執行查詢任務,再將查詢結果一次回傳給客戶端。
透過組合模式,客戶端可以在單個請求中取得跨越多個小型服務的資料。通常,閘道器模式也會實作組合模式,這種模式並非單純的將請求轉發到其他服務中,而是透過特定的組合邏輯,將多個服務的 請求結果合併為單個請求,使客戶端能夠更有效率的從多個服務中取得資料。
對於高階層的開發人員而言,當實作一個複雜的業務邏輯時,可能是一個跨越不同服務且具有一定順序並互相影響的過程。這些與微服務溝通的過程不可能僅靠直接連線進行處理。因此,發展出了服務編排模式以及服務協作模式。
微服務彷彿訓練有素的舞者,自覺地知曉現在得做什麼。
在服務編排模式下,複雜的任務執行將會由服務自行負責。透過這個模式,業務邏輯將會解開耦合並分散到各自的服務中。透過實作這個設計模式,服務將能夠主動通知下一個任務的執行,所有的服務將會關注自己應該執行的部分。就像觀看一場多人舞蹈表演一樣,舞者們有著相同的目標,透過各自的舞蹈動作完善了整場演出。
如上圖所示,通常服務編排模式多採用基於事件的發佈與訂閱模式來達成。藉由訂閱固定的主題來觸發任務處理,同時在處理完畢後發佈最新的狀態。藉由事件發佈與訂閱的機制,讓所有的微服務能夠及時地開始處理自己所負責的部分。
邏輯的解耦與職權分割明確是服務編排模式的一大特點,但在服務編排模式下的業務邏輯將會分散於參與編排的服務之中,對開發人員而言將無法綜觀整個複雜業務是怎麼運作的。同時,在一個依賴不同微服務的業務中,若參與其中的微服務發生問題,將會難以追蹤。
使用服務編排設計模式具備較高的門檻,除了在架構上需要更有經驗的工程師進行規劃。並且,發佈與訂閱模式也並非是所有軟體工程師都熟悉的設計模式,若團隊能力要求允許,採用服務編排設計模式才得以減少溝通與教育的成本。
就像精心設計的音樂會,微服務將完全聽從你的指揮
在服務協作模式下,你的微服務只提供基本的資源操作介面(例如:CRUD RESTful API),他們將隨時預備好執行你所需要的工作。服務們並不關注任何複合式的業務邏輯,它們只在乎自身資源的控制,被動的等待任何請求的發生,就像傳統的 Client-Server 應用程式一樣。在服務協作模式中,你就像是一場音樂會的指揮家,指揮著所有服務的運作。
服務協作模式將服務執行的順序、邏輯判斷以及錯誤時的例外處理統一由協作器(Orchestrator)進行指揮。因為業務邏輯的集中,所有溝通的發生都是由協作器呼叫參與的服務,而參與的服務並不會主動地協作器產生關係,也不會了解到其它服務的存在,這使得服務間的依賴關係更加地單純。
同時,微服務在複雜業務下的執行順序、處理邏輯將被統一地撰寫。透過實作出服務協作模式,開發者在通盤了解某個業務的全貌將會比起編排模式更加簡單快速。從另一個角度來看,實作服務編排模式會將業務邏輯分散於參與的服務之中,對開發人員而言將會有更高的維護難度。
在複雜的業務場景時,服務協作模式相比編排模式來說可能會因為頻繁的溝通而造成效能損耗。但因其將執行邏輯整合在單一協作器且直觀的特性,會更適合傳達概念以及維護。
雖然服務協作設計模式看似簡單且好實作,但有可能在設計上讓人頭疼。根本地來說,將邏輯集中在單一協作器是一種耦合的行為,雖然並不是所有的耦合都是壞事,但此種耦合可能會造成開發者過於依賴協作器集中的特性,讓某些本來應該由微服務本身處理的邏輯集中到協作器之中。因此,實作此種模式則需要在開發時期及時梳理協作器中的業務邏輯,適時地讓某些不適當的邏輯回歸服務中,避免開發出包含大量額外業務邏輯的協作器。
在本章中,我們綜觀了以「溝通」為主題的微服務設計模式,這也是選擇採用微服務設計模式的你需要面對的首要課題。選擇不同的設計模式並沒有絕對,我們永遠都在「效能」、「維護難度」與「技術能力」等惱人的課題中來回踱步與反覆評估。
第一章所推薦的微服務相關書籍都有提到上述設計模式,以及各自的優缺點,也有不同的看法。因此,我非常推薦你去看看這些書籍,也希望能夠幫助到你。
[1] C.Richardson, Microservices Patterns, Manning Publications, 2017.