微服務架構中服務集成的主要技術

在微服務架構中,服務之間勢必需要集成,而這種集成關係遠比簡單的API調用要複雜。在本文中,我們將系統分析服務集成的方式以及在微服務架構中的表現形式。關於服務之間的集成存在一些通用的模式,我們也將在梳理這些模式的同時給出實現過程中的最佳實踐。   

業界關於系統集成存在一些主流的模式和工程實踐,包括文件傳輸(FileTransfer)、共享數據庫(Shared Database)、遠程過程調用(RPC)和消息傳遞(Messaging)。這四種主流的集成模式各有優缺點。文件傳輸方式最大的挑戰在於如何進行文件的更新和同步;如果使用數據庫,在多方共享的條件下如何確保數據庫模式統一是一個大問題;RPC容易產生瓶頸節點;而消息傳遞在提供松耦合的同時也加大了系統的複雜性。RPC和消息傳遞面對的都是分佈式環境下的遠程調用,遠程調用區別於內部方法調用,一方面網絡不一定可靠和存在延遲問題,另一方面集成通常面對的是一些異構系統。

對於微服務架構而言,我們的思路是儘量採用標準化的數據結構並降低系統集成的耦合度。我們會根據需要採用上圖中所示的四種典型的系統集成模式,同時還會引入其它一些手段來達到服務與服務之間的有效集成。個人把微服務架構中服務之間的集成模式分爲如下圖所示的四大類。

  • 接口集成

接口集成是服務之間集成的最常見手段,通常基於業務邏輯的需要進行集成。RPC、REST、消息傳遞和服務總線都可以歸爲這種集成方式。

  • 數據集成

數據集成同樣可以用於微服務之間的交互,共享數據庫是一個選擇,但也可以通過數據複製的方式實現數據集成。

  • 客戶端集成

由於微服務是一個能夠獨立運行的整體,有些微服務會包含一些UI界面,這也意味着微服務之間也可以通過UI界面進行集成。

  • 外部集成

這裏把外部集成單獨剝離出來的原因在於現實中很多服務之間的集成需求來自於與外部服務的依賴和整合,而在集成方式上也可以綜合採用接口集成、數據集成和UI集成。

接下來我們將對這四大類集成策略展開討論,給出對應的實現技術的簡單描述。

(1)接口集成

首先我們討論一下RPC。RPC(Remote Process Call,遠程過程調用)架構是服務之間進行集成的最基本方式。我們可以對RPC架構進行剖析,得到下圖的結構圖,該結構圖包括了微服務之間在分佈式環境下交互時所需的各個基本功能組件。

從上圖中,可以看到RPC架構有左右對稱的兩大部分構成,分別代表了一個遠程過程調用的客戶端和服務器端組件。客戶端組件與職責包括負責編碼和發送調用請求到服務方並等待結果、負責維持客戶端和服務端連接通道和發送數據到服務端等;而服務端組件與職責則包括負責接收客戶方請求並返回請求結果和負責調用服務端接口的具體實現並返回結果等。對於客戶端和服務器端而言,都需要負責網絡傳輸協議的編碼和解碼。目前業界也存在很多優秀的RPC框架,如應用非常廣泛的Apache Dubbo等。  

說道RPC,就不得不提REST。REST(Representational State Transfer,表述性狀態轉移)從技術上講也可以認爲是RPC架構的一種具體表現形式,因爲RPC架構中最基本的網絡通信、序列化/反序列化、傳輸協議和服務調用等組件都能在REST中有所體現。但REST代表的並不是一種技術,也不是一種標準和規範,而是一種設計風格。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。要理解RESTful架構,最好的方法就是去理解它的全稱Representational StateTransfer這個詞組,直譯過來就是“表現層狀態轉移”,其實它省略了主語。“表現層”其實指的是“資源”的“表現層”,所以REST通俗來講就是:資源在網絡中以某種表現形式進行狀態轉移。主流的Spring Cloud採用的就是基於HTTP協議和RESTful風格的交互方式。

從軟件設計的耦合度上講,無論是RPC還是REST都存在一定的耦合度問題。就RPC而言,存在三種耦合度,即技術耦合、空間耦合和時間耦合,如下圖所示:

上圖中,技術耦合度表現在服務提供者與服務消費者之間需要使用同一種技術實現方式,如(a)中服務提供者與服務消費者都使用RMI作爲通信的基本技術,而RMI是Java領域特有的技術,也就意味着其它服務消費者想要使用該服務也只能採用Java作爲它的基本開發語言;空間耦合度指的是服務提供者與服務消費者都需要使用統一的方法簽名才能相互協作,(b)中的getUserById(id)這個方法名稱和參數的定義就是這種耦合的具體體現;而時間耦合度則表現在服務提供者與服務消費者兩者只有同時在線才能完成一個完整的服務調用過程,如果出現圖(c)中所示的服務提供者不可用的情況,顯然服務消費者調用該服務就會發生失敗。

對於REST而言,情況相對會好一點。基於HTTP的面向資源的架構風格能夠支持在服務提供者與服務消費者之間採用多種不同的技術實現方式,從而規避技術耦合度。而對於空間耦合,也可以採用HATEOAS一定程度上緩解這種耦合度。但在時間耦合度上,REST風格面臨與RPC同樣的場景和問題。

消息傳遞(Messaging)機制能夠降低技術、空間和時間耦合。如下圖所示,消息傳遞機制在消息發送方和消息接收方之間添加了存儲轉發(Storeand Forward)功能。存儲轉發是計算機網絡領域使用最爲廣泛的技術之一,基本思想就是將數據先緩存起來,再根據其目的地址將該數據發送出去。顯然,有了存儲轉發機制之後,消息發送方和消息接收方之間並不需要相互認識,也不需要同時在線,更加不需要採用同樣的實現技術。緊耦合的單階段方法調用就轉變成松耦合的兩階段過程,技術、空間和時間上的約束通過中間層得到顯著緩解,這個中間層就是消息傳遞系統(MessagingSystem)。

在消息傳遞系統中,消息的發送者稱爲生產者(Producer),負責產生消息,一般由業務系統充當生產者;消息的接收者稱爲消費者(Consumer),負責消費消息,一般是後臺系統負責異步消費。生產者行爲模式單一,而消費者根據消費方式的不同有一些特定的分類,常見的有推送型消費者(PushConsumer)和拉取型消費者(Pull Consumer),推送指的是應用系統向消費者對象註冊一個Listener接口並通過回調 Listener 接口方法實現消費消息,而在拉取方式下應用系統通常主動調用消費者的拉消息方法消費消息,主動權由應用系統控制。在微服務架構中,我們同樣需要有一套能夠提供消息傳遞功能的工具和框架從而實現消息驅動的服務開發和交互能力。這方面的工具也很多,例如RabbitMQ、Kafka、RocketMQ等。

與消息傳遞相關的另一個技術是服務總線。服務總線(Service Bus)本質上也是一種系統集成組件,用於解決分佈式環境下的異步協作問題,可以看作是對消息傳遞系統的擴展和延伸。使用服務總線的典型需求包括:

  • 將消息路由到一個或多個目的地

  • 將消息轉化爲另一個表現形式

  • 執行消息的分解和聚合功能,即能夠實現消息的分解並將分解後的消息發到目的地之後再進行組裝

  • 使用發佈-訂閱模式來提供動態內容等。

圍繞這些需求,ESB提供了實現這些需求的核心組件,包括路由器(Router)、轉換器(Transformer)和端點(Endpoint)。對於這些組件本文不做具體展開。服務總線也可以看做是一種規範,業界基於如何實現服務總線提供了多種第三方工具,如MuleESB、Apache Camel和Spring Integration。這些工具都爲我們提供了強大而齊全的端點集成機制,同時通過封裝簡化了這些端點的使用方式。以Spring家族的Spring Integration爲例,該工具爲我們提供的常見集成端點包括File、FTP、TCP/UDP、HTTP、JDBC、JMS、JPA、Mail、MongoDB、Redis、RMI、WebServices等不下數十種,且各個集成端點在使用方式上大同小異。實際上,SpringCloud Stream中的Spring Cloud Stream就是構建在Spring Cloud Integration之上。

(2)數據集成

如果共享數據庫,數據的存儲和表現形式不容易被修改和重構,因爲有很多系統對這些數據持有訪問權限。一旦對數據做出修改,就可能導致其中一個或多個系統不能正常運作。這就意味着對數據的修改需要協調各個應用系統,這顯然會影響到系統的可擴展性。另一方面,這也會導致無法對系統功能進行快速迭代,而業務的快速迭代正是微服務架構所應具有的特性。

對共享數據庫最難以把控的一點是如何統一數據庫模式(Scheme)。試想如果一個應用系統需要刪除某張表中的某個字段,對於普通的場景而言,這無疑是非常簡單的事情。但對於共享的數據庫而言,由於不知道其它系統是否還在使用該字段,所以也就無法進行直接刪除。如果這樣的場景很多,那麼隨着時間的推移,數據的複雜性和可維護性都會對系統造成很多影響。

共享數據庫顯然不能滿足微服務架構中的集成需求,在微服務架構中,我們追求數據的獨立性。但對於一些遺留系統而言,我們無法重新打造數據體系,數據複製(DataReplication)就成爲一種折中的集成方法。所謂數據複製,就是在不同的數據容器中保存同一份業務數據。這裏的同一份業務數據的概念不在於說數據內容的完全一致性,而是在於這些數據背後的業務邏輯的一致性。

實現數據複製的關鍵就在於打破數據庫模式的限制。當我們採用某個方式在兩個數據存儲容器中同時存放兩份數據時,如果它們的數據庫模式是完全一樣的,那麼還是會碰到共享數據庫模式下的諸多問題。當我們在實施數據複製的集成方式時,將某一份數據庫模式轉化成其他服務所需要的形式然後再進行數據同步是一項最佳實踐。

有了數據複製的設計理念,接下去就要考慮數據冗餘所帶來的數據一致性(Consistency)的問題。我們明確數據的實時一致性通常都是不需要的,所以可以採取最終一致性(Eventually Consistency)的方式實現數據複製。實現數據複製有兩種基本策略,一種是批量操作,一種是事件。

批量(Batch)操作一般通過定時任務的方式在某一個時間點對一批符合複製要求的數據進行同步操作。在實現上,批量操作最好能夠支持全量和增量操作,同時爲每一批數據確定一個全局唯一的版本號。通過版本號,數據集合就具備選擇性和去重性,也就意味着批量操作是一個可以重複執行的過程。批量操作具備一定風險性,由於批量操作本身無法持有狀態,利用版本號把狀態放到數據中去。另一方面,在數據的接收方,確保採用一定的數據適配機制實現解耦。採用批量操作實現數據複製的結構圖參考下圖,這裏的數據倉庫泛指包含關係型數據庫在內的各種數據存儲媒介。

而對於事件而言,需要將所產生的數據建模成一系列離散事件,我們可以藉助於消息傳遞系統達到數據同步的目的。相比批量操作,事件驅動的數據複製機制能達到較高的數據一致性要求。事件發送方相對簡單,只需要將所產生的事件放入事件發佈器即可,但對於事件的訂閱者而言,可能存在多種表現形式。不管基於何種訂閱者模式,在技術實現上我們都可以藉助前面介紹的消息傳遞機制達到基於事件的數據複製效果。

 (3)客戶端集成

當微服務數量較多且客戶端集成場景比較複雜時,通常就需要單獨抽取一層作爲客戶端訪問的統一入口,這一層在微服務架構裏有個專門的叫法稱之爲API網關(Gateway)或服務網關。API網關的主要作用是對後端的各個微服務進行整合,從而爲不同的客戶端提供定製化的內容。API網關是微服務架構的基礎組件。

BackEndFor FrontEnd(BFF)服務器是對API網關更爲形象的叫法,也就是專門爲前端服務的後端服務器。該服務器在定位上只應該是很薄的一層,不應該包含任何與業務相關的邏輯和實現。同時,如果整個系統非常龐大,所有的服務集成都放在一起也會加重這層的維護成本,所以針對不同的業務體系提供專門的BackEndFor FrontEnd服務器也是集成過程中的一項最佳實踐。下圖展示的就是BackEndFor FrontEnd服務器應用的示例,可以看到系統中存在移動後端、門戶後端和管理後端三種BackEndFor FrontEnd服務器組件,分別面向移動應用、門戶網站和內部管理系統。

在BackEnd For FrontEnd服務器上,由於服務數量大,修改和發佈的頻率也可能很高,微服務所提供的接口變化管理上通常採用逐步遷移的方案(見下圖)。

(4)外部集成

隨着服務化思想以及SaaS(SoftwareAs A Service,軟件即服務)應用的日漸增多,與外部系統進行集成的方式也發生了很多變化。在服務集成領域,目前基於服務回調的集成方式應用非常廣泛。

對於集成方式,實際上也可以簡單理解爲服務與服務的集成,只不過有些服務是來自第三方平臺。回調作爲消除循環依賴的一種有效方式,只需要我們提供回調入口即可完成與外部系統的集成。整個服務交互過程中,在服務訪問入口添加防腐層是一項最佳實踐。而防腐層的建立通常需要實現適配(Adapt)和轉換(Convert)。考慮這樣一個場景,當外部系統通過基於RESTful風格暴露訪問接口給我們時,我們在使用該服務時,就需要考慮如何獲取通過HTTP協議傳輸的數據以及如何將這些數據轉換爲該系統自身所能識別的業務數據。下圖展示了該場景下防腐層的實現過程。同樣,對於我們所提供的供外部系統訪問的回調接口,防腐層的設計理念同樣適用。

本文對微服務架構中服務之間如何集成的相關技術做了簡要綜述,希望對大家有所幫助。

更多內容可以關注我的公衆號:程序員向架構師轉型。

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