作者:人月神話,新浪部落格同名

簡介:多年SOA規劃建設,私有云PaaS平臺架構設計經驗,長期從事一線專案實踐

今天準備談下微服務架構下各個微服務間如何解耦,以及對於已經緊耦合的微服務如何進行重構。在談這個內容前,可以先看下我前兩天釋出的微服務模組和粒度如何劃分才更加合理的一篇文章,這篇文章對於微服務拆分有比較詳細的描述。

要明白實際上微服務後續出現的諸多問題往往都是一開始微服務模組劃分就不合理導致,對於具體的模組劃分方法和原則,我在上面文章裡面給出了以下幾點。

原則1:劃分為<10個微服務模組

原則2:強資料關聯模組不要拆分

原則3:以資料聚合驅動業務功能聚合

原則4:從縱向功能劃分思路到橫向分層思路轉變

原則5:高內聚,松耦合的基礎原則

對於具體的內容在這篇文章不再重複給出。可以看到對於微服務模組拆分更多的是屬於業務建模和系統分析方面的內容,而今天談的微服務解耦重點是想從可用的技術手段入手來談下可用的以下解耦方法和策略。

問題綜述

微服務架構下如何解耦,對於已經緊耦合下如何重構?

最近幾年對於微服務架構,企業中臺構建,元件化和服務化,平臺+應用構建模式,包括Docker容器化,DevOps等都越來越受到傳統企業的關注,也可以看到很多企業傳統架構也在朝這個目標進行演進和轉型。對於微服務架構本身的優點和缺點,包括傳統企業實施微服務架構的演進路線等,在我前面很多微服務架構相關的文章中都有所介紹,今天主要談下在微服務架構下的解耦問題。

要明白,企業實施微服務架構後,原來所有內部的介面呼叫,內部的完整事務都會變成微服務模組間的跨域的介面服務呼叫,傳統事務也變成了分散式事務,這些本身是增加了系統複雜度。

原來一個系統就能夠完成的事情,現在要依賴底層

技術元件

服務,其它業務微服務模組多個Http Rest API介面呼叫往往才能夠完成。只要其中任何一個

API介面

出現問題,都會直接影響到前端的業務功能使用。

微服務間的雪崩效應:在採用微服務架構後,各個微服務間存在大量的API介面服務呼叫,相互之間還形成了服務呼叫鏈,比如A-》B-》C,那麼如果C服務出現故障就將直接影響到B服務無法正常訪問和服務阻塞,同時B的故障又將進一步傳導到A服務的消費和使用。

對於網際網路企業實施微服務架構,其中有幾個關鍵點。

其一就是微服務架構可以更好的進行平臺的效能擴充套件和高伸縮要求。

其二就是網際網路應用本身業務規則相對簡單,模組間容易解耦。

其三就是大的網際網路企業IT技術積累更強,有更好的技術能夠搭建高可用的技術平臺,也有更好的技術能夠實現微服務架構實施後的自動化運維和監控。

而這些往往都是傳統企業在實施微服務架構所欠缺的,比如有些企業一開始實施微服務架構沒發現問題,結果最終上線後卻發現後續的系統運維和效能監控,故障問題分析和排查等能力跟不上,無法及時響應客戶需求並快速的定位和解決問題。

即前面經常說到的,傳統企業的IT治理和團隊技術能力跟不上,直接影響到微服務架構實施成敗。

那麼回到正題,今天希望討論和分析下,企業實施微服務架構後,如何儘量減少微服務模組的耦合導致的單個微服務模組功能實現和執行故障,簡單來說就是一個微服務模組中業務功能的執行,如何做到最小化的依賴外部微服務模組Http API服務介面的可用性。即使外部模組掛點,當前模組也能夠正常使用,或者說能夠不影響到當前模組核心功能的使用。

對於該問題,我們可以分開從幾個方面進行討論。

同步呼叫轉為非同步呼叫

一說到解耦,我們一定會首先想到訊息中介軟體來實現非同步,即將同步轉為非同步,透過非同步來實現解耦。我們可以先想訊息傳送給訊息中介軟體,只要訊息中介軟體是高可用性的沒有宕機,整個介面整合過程就是OK的,而

訊息中介軟體

再非同步方式分發訊息給目標系統,同時支援重試。

訊息中介軟體的採用

微服務架構下如何解耦,對於已經緊耦合下如何重構?

訊息中介軟體(message oriented middleware)是指支援與保障分散式應用程式之間同步/非同步收發訊息的中介軟體。訊息是分散式應用之間進行資料交換的基本資訊單位,分散式應用程式之間的通訊介面由訊息中介軟體提供。其中,非同步方式指訊息傳送方在傳送訊息時不必知道接收方的狀態,更無需等待接收方的回覆,而接收方在收到訊息時也不必知道傳送方的目前狀態,更無需進行同步的訊息處理,它們之間的連線完全是松耦合的,通訊是非阻塞的,這種

非同步通訊

方式是由訊息中介軟體中的訊息佇列及其服務機制保障的。

訊息中介軟體實現了釋出者和訂閱者在時間、空間和流程三個方面的解耦:

時間解耦—-釋出方和訂閱方無需同時線上就能夠進行訊息傳輸,訊息中介軟體透過儲存轉發提供了這種非同步傳輸的能力;

空間解耦——釋出方和訂閱方都無需知道對方的物理地址、埠,甚至無需知道對方的邏輯名字和個數;

流程解耦——釋出方和訂閱方在傳送和接收資料時並不阻塞各自的控制流程。

從訊息中介軟體的基本功能來看,無論是點對點訊息中介軟體還是訊息代理,其體系結構都是非常清晰簡單的。但由於分散式應用及其環境的多樣性和複雜性,導致了訊息中介軟體的複雜性。

當前的訊息中介軟體仍然分為兩類,一類是基於AMQP高階訊息協議的,一類是基於JMS訊息協議的。對於網際網路使用較多的RabbitMQ,Kafka等基本都基於AMQP高階訊息協議。而對於Weblogic JMS,IBM MQ則是基於JMS訊息協議的訊息中介軟體產品。

對於Weblogic而言是一個企業級的應用伺服器中介軟體,同時Weblogic JMS也是企業級的訊息中介軟體產品,該產品是一個企業級訊息中介軟體產品,具備了高可靠,高可用,高擴充套件,高效能基礎特性。支援主流的各種訊息模型,訊息釋出訂閱,訊息持久化,事務處理,叢集等核心特性。

訊息中介軟體的使用場景,具體包括瞭如下幾個方面:

訊息通知:單據狀態變化後的事件通知,資料傳輸完成後的事件通知

非同步整合:服務消費方只需要將資料送到OSB即實時返回,透過非同步整合實現徹底解耦

目標系統削峰

:大併發資料匯入而目標系統處理效能受限的場景

訊息釋出訂閱:基礎主資料透過JMS實現1對多的實時資料分發

高可靠性場景:確保在資料整合中不出現任何丟失的情況

對於採用Weblogic JMS來實現訊息整合,具體過程如下圖:

微服務架構下如何解耦,對於已經緊耦合下如何重構?

基於事件驅動的業務分析

而要做到同步轉非同步,我們必須從業務需求分析開始就轉變思維,即從傳統的業務流程需求分析方法轉到事件驅動分析方法,這個在我很早的EDA事件驅動架構內容整理的時候專門談到過,今天摘錄部分內容供大家參考。

事件驅動框架(EDA)裡事件可傳輸於鬆散耦合的元件和服務之間。一個事件驅動系統典型地由事件消費者和事件產生者組成。事件消費者向事件管理器訂閱事件,事件產生者向事件管理器釋出事件。當

事件管理器

從事件產生者那接收到一個事件時,事件管理把這個事件轉送給相應的事件消費者。如果這個事件消費者是不可用的,事件管理者將保留這個事件,一段間隔之後再次轉送該事件消費者。

EDA架構往往具備如下特徵:

廣播通訊:參與通訊的系統將事件廣播給任何對該事件感興趣的參與者。

實時性:在業務事件發生時候,EDA架構下可以實時的傳送事件給消費方,而無需等待

非同步:事件釋出系統不用等待事件接收系統來處理事件,傳送到EDA模組即可返回。

細粒度:只要具備獨立的業務價值,即可以釋出為細粒度的事件,而不是傳統服務下的粗粒度。

複雜事件處理:根據業務流程需求,事件間可以聚合和組裝,形成事件鏈滿足複雜事件處理。

並行執行:多個事件可以同時執行,單個事件可以同時分發給多個訂閱方。

非阻塞:EDA本身提供MQ等訊息持久化機制,不會在事件大併發下出現事件阻塞情況。

簡單來講,訊息整合,非同步,徹底解耦,訊息釋出訂閱,事件鏈是EDA整個架構的核心。但是在EDA包括CEP複雜事件處理,在使用的時候首先還是應該瞭解清楚其和傳統流程驅動在業務分析方法上的區別。簡單來說,流程驅動和事件驅動的一個簡單比較可以用下圖描述:

微服務架構下如何解耦,對於已經緊耦合下如何重構?

基於EDA的核心業務分析思路說明

在事件驅動架構下,業務分析的核心就是事件的識別。而對於傳統方法往往則是關鍵流程和活動即可。在總體分析思路上的變化來說,傳統分析方法只分析到第2-3級業務流程,識別業務活動和互動點,而EDA需要業務分析時候分析到L4級的最底層EPC

事件流程圖

,並識別關鍵業務事件和事件分解,聚合關係。

在具體分析內容上的變化來說,傳統方法只關心業務活動,而不關係業務活動具體的啟動機制,業務活動完成後產生的業務事件。基於EDA業務分析方法,需要開啟業務活動,識別業務活動的前者觸發條件和業務活動引起的業務物件狀態的變化,往往狀態變化點都是關鍵的事件識別點。

具體可以用下圖進行描述:

微服務架構下如何解耦,對於已經緊耦合下如何重構?

簡單事件-基於業務需求用例分析和識別

業務事件

的識別可以從業務需求用例入手,分析業務用例中的業務前置觸發條件,分析業務物件的狀態流轉過程和後續操作,以找尋業務活動的事件輸入和事件產生。

從下圖裡面也可以看到,對於事件的識別往往比用例的識別更加細化,需要詳細的分析業務用例中的基本流,擴充套件流,業務規則,特別是關注其中核心的業務物件和單據狀態的變化。同時對於用例分析中的觸發條件也需要重點分析,這些觸發條件往往是事件鍊形成,或者說觸發訊息事件訂閱的來源。

微服務架構下如何解耦,對於已經緊耦合下如何重構?

複雜事件-基於事件識別形成事件鏈

傳統的基於流程的業務分析方法往往只會分析到業務流程,具體的業務活動,而不關心具體業務活動執行前或執行後產生的業務事件,這和介面平臺前期重點關注資料整合有關係。而為了保證業務實時響應需求,必須準確的識別業務事件,才能進一步設計基於業務事件的處理和響應機制。

基於EPC事件流程鏈分析思路,需要對傳統分析流程進行細化,增加紅色事件識別點和事件分解聚合關係。在事件鏈的形成過程中往往存在一些複雜場景需要分析,包括了事件的一對多分發和訂閱,也包括了多個事件聚合,在滿足某個特定的業務規則後才觸發下一個新的業務活動和新事件。這些都是在複雜事件分析中必須考慮的內容之一。

微服務架構下如何解耦,對於已經緊耦合下如何重構?

從EDA事件驅動到CQRS

微服務架構下如何解耦,對於已經緊耦合下如何重構?

顧名思義,CQRS即命令查詢職責分離,將CUD操作和R查詢操作分離,對於CUD操作仍然參考傳統的領域模型建模思路來實現,但是在命令中增加了

訊息事件機制

,實現CUD操作變更透過訊息事件非同步寫入到資料庫。

在CQRS中,查詢方面,直接透過方法查詢資料庫,然後透過DTO將資料返回,這個方面的操作相對比較簡單。而命令方面,是透過傳送具體Command,接著由CommandBus來分發到具體的CommandHandle來進行處理,CommandHandle在進行處理時,並沒有直接將物件的狀態儲存到外部持久化結構中,而僅僅是從領域物件中獲得產生的一系列領域事件,並將這些事件儲存到Event Store中,同時將事件釋出到

事件匯流排Even

t Bus進行下一步處理;接著Event Bus同樣進行協調,將具體的事件交給具體的Event Handle進行處理,最後Event Handler把物件的狀態儲存到對應Query資料庫中。

微服務架構下如何解耦,對於已經緊耦合下如何重構?

對於CQRS,最容易想到的還是在資料庫層面做的讀寫分離模式,可以看到CQRS本身和資料庫的讀寫分離模式可以更好的匹配,由於採用事件驅動和訊息訂閱模式,對於R讀庫我們可以更加容易對資料變更資訊進行更新,達到讀庫資料的及時同步更新。同時讀庫既可以採用讀寫分離資料庫,也可以採用類似Solr,Nosql等分散式,非結構化資料來實現彈性水平擴充套件能力。

在命令查詢職責沒有分離的時候,可以看到一方面是模型本身的擴充套件性受到影響,另外一方面是原有的領域模型本身偏重,而且Entity實體本身也透過完整的DTO物件進行傳輸,這樣在一些特殊的只需要更新或查詢個別欄位的時候,整個模型仍然偏重。

透過命令查詢職責的解耦,不僅僅是提升整個框架模型的擴充套件性,更加重要是將兩類業務規則和實現徹底的解耦開,方便後續的功能開發和運維,特別是在整個業務場景和邏輯實現複雜的情況下,這種解耦會使整個開發架構更加清晰簡單。

同時也可以看到有一Command命令都是採用非同步事件的方式進行寫入,因此不存在同步和長連線佔用的問題,有利於提升整個平臺在大併發下的整體響應效能。

當然,採用CQRS模式最大的一個問題點就是無法實現命令和查詢兩部分內容的強一致性保障,即很可能你介面上查詢到的資料不是最新的持久化資料庫裡面的資料,這個本身和訊息管道非同步寫入的實時性有關係。

其次在使用

CQRS模式

的時候,有一個重要假設就是,在事件和命令發出後,無特殊情況在事件接收方都必須要能夠接收事件成功處理,否則就存在大量的異常錯誤訊息的非同步回寫,反而增加系統的複雜度。舉個簡單例子來說:

當我們在電商平臺購買一個商品的時候,只要訂單提交成功,那麼這個訂單就一定能夠生效,也一定有庫存能夠發運和配送,而不是在後續到了配送環節的時才發現沒有庫存而導致訂單取消。如果這樣的話就極大的降低了系統本身的易用性。

即在非同步命令和事件傳送場景,當命令傳送成功時候,雖然我們沒有及時接收到處理方的事件處理結果資訊,但是我們預設是接收方能夠成功處理事件。但是我們也看到在CQRS場景框架下,只要

命令事件

發出,我們並不需要等待任何反饋資訊。

另外還有一種CQRS實現場景,即雖然在內部對Command命令處理的時候是基於事件機制,非同步響應,但是客戶在前端的操作是同步等待返回。在這種情況下我們就可以保持前端連線,但是是否後端的類似DB連線等。

在CQRS模型下,由於職責分離,可以看到我們透過事件和訊息的訂閱,可以實現多個讀庫的訂閱,這些讀庫既可以是結構化資料庫,也可以是非結構化資料庫;既可以用來實現業務功能本身的查詢讀,也可以用來做海量資料本身的分散式全文檢索。

對於CQRS框架的實施,不是簡單的設計模式使用問題,更加重要的仍然是是否能夠接受最終一致性要求,同時在該要求下將傳統的同步請求下業務功能和邏輯處理機制轉變為非同步事件價值下的事件鏈驅動模式。要實現這種轉變就必須能夠拆分出獨立,自治的命令和事件,同時確保這些事件在朝後端業務功能和邏輯模組傳送的時候能夠處理成功(即該做的校驗必須提前做完)。

將同步介面呼叫轉為本地訊息快取

這個類似訊息中介軟體的功能,舉例來說我們設計了一個同步傳送訂單到ERP系統的介面,如果在同步實時呼叫這個介面服務的時候出現異常,那麼我們可以首先將訊息儲存到本地,然後設定定時任務和重試機制,透過重試方式將訊息傳送到目標系統。

即對於業務功能來說不用關心實時是否傳送成功,而由業務系統自身機制來完成訊息傳送的重試。

而要做到這點,在介面功能設計時候,最好要做到單據業務完整性校驗介面和實際的資料傳送介面分離,即先呼叫介面進行完整性校驗,在校驗沒有問題後再進行訊息傳送。以確保最終傳送的訊息不會因為資料完整性的原因導致無法傳送成功。

查詢資料的本地化快取或落地

memcached是一套分散式的快取記憶體系統,由LiveJournal的Brad Fitzpatrick開發,但被許多網站使用。這是一套開放原始碼軟體,以BSD license授權釋出。

memcached的API使用三十二位元的迴圈冗餘校驗(CRC-32)計算鍵值後,將資料分散在不同的機器上。當表格滿了以後,接下來新增的資料會以

LRU機制

替換掉。由於memcached通常只是當作快取系統使用,所以使用memcached的應用程式在寫回較慢的系統時(像是後端的資料庫)需要額外的程式碼更新memcached內的資料。

對於實時查詢類介面,將查詢的基礎資料進行本地化快取,即如果在實時查詢出現異常的時候,我們可以直接查詢本地快取的資料,減少對業務功能使用的影響。

比如查詢供應商介面服務,如果主資料系統提供的接口出現異常,我們可以直接查詢本地快取的

供應商資料

。這種模式對於變更不頻繁的資料基本都適應,同時本身也減少實時呼叫介面帶來的效能損耗。

如果是介面服務註冊在API閘道器或ESB

服務匯流排

上面,我們還可以考慮在ESB服務總線上啟用快取能力,即對於呼叫過的介面,在同樣引數重複呼叫的時候能夠透過快取資料獲取,這樣即使在源端業務系統不可用的情況下,也不好影響到當前介面服務的成功呼叫。

可以適度考慮資料落地

在微服務架構裡面,我們一直在強調一點,即資料實時需要實時訪問,不進行底層資料庫的資料整合和同步,這既滿足了資料的高一致性,也滿足了資料實時性的要求。

但是帶來的問題就是強耦合,如果資料提供方出現異常,那麼導致消費方業務功能也無法使用。

因為我們可以適量考慮資料落地方式的資料整合在整體微服務架構實施過程中,對於變化不頻繁的資料適度落地到微服務模組本地。這樣本身可以減少實時的業務介面服務呼叫,增加單個微服務模組的可用性和可靠性。

對於已經出現強耦合如何重構

如果微服務已經實施完成並出現了大量緊耦合的情況,那麼我們就需要在後期考慮對微服務架構進行重構,具體重構的方法可以從如下幾點考慮。

兩個微服務本身緊耦合

微服務架構下如何解耦,對於已經緊耦合下如何重構?

如果兩個微服務間出現大量介面相互呼叫,即可以認為緊耦合。

或者我原來的判斷標準,即兩個微服務對應的後臺資料表,其中30%以上都需要兩個微服務交叉訪問,那麼就認為兩個微服務本身耦合性極強。

在這種情況下處理措施就是原來微服務劃分的太細了,需要對兩個微服務進行合併。

交叉依賴變為共性依賴

微服務架構下如何解耦,對於已經緊耦合下如何重構?

要知道在傳統軟體開發裡面往往是不允許兩個元件交叉依賴的。

但是在新的IOC和微服務開發裡面,大量都是反射呼叫,兩個元件相互依賴不會有問題。但是這本身也不是一種很好的設計方法。

如果兩個微服務或多個微服務相互依賴內容本身具備共性。那麼最好的做法就是將共性內容全部移出,下沉為一個共性基礎微服務模組再朝上提供服務。

即交叉依賴轉變為對底層的共性依賴。

對某個微服務實現單元進行遷移

微服務架構下如何解耦,對於已經緊耦合下如何重構?

為什麼出現這種場景?

簡單來說就是原來的微服務模組劃分和業務功能劃分不合理。比如上圖中的微服務A中的A1部分。這個部分內容需要大量被微服務B呼叫,但是A1實際依賴微服務A中其它部分的內容卻很少。

這種就是典型的A1部分功能劃分位置不合理。

最好的做法就是將A1功能從微服務A遷移到微服務B,實現對原有業務劃分不合理的糾正。

將細粒度服務轉變為粗粒度服務

微服務架構下如何解耦,對於已經緊耦合下如何重構?

服務本身應該具備粗粒度屬性,暴露僅僅需要暴露的內容。

比如微服務A實現客戶信用檢查和評級。微服務B需要客戶信用。有兩種做法

第一種是B呼叫A多個介面,把客戶基本資訊,客戶交易資訊,客戶違約資訊全部查詢過來,然後自己計算客戶信用。

第二種即是隻需要輸入客戶編碼,微服務A返回最早的信用評級。

對於後者就是我們常說的粗粒度介面或領域服務,服務間的互動應該以領域服務和粗粒度服務為主,避免掉完全的資料庫表的CRUD類服務介面。

資料分享

12 套

微服務、Spring Boot、Spring Cloud 核心技術資料,這是部分資料目錄:

Spring Security 認證與授權

Spring Boot 專案實戰(中小型網際網路公司後臺服務架構與運維架構)

Spring Boot 專案實戰(企業許可權管理專案))

Spring Cloud 微服務架構專案實戰(分散式事務解決方案)

公眾號後臺回覆arch028獲取資料::

微服務架構下如何解耦,對於已經緊耦合下如何重構?