《微服務設計》第 4 章 集成

  • 2019 年 10 月 8 日
  • 筆記

  • 集成是微服務相關技術中最重要的一個。做得好的話,你的微服務可以保持自治性,你也可以獨立地修改和發佈它們;但做得不好的話會帶來災難

4.1 尋找理想的集成技術

4.1.1 避免破壞性修改

  • 如果一個微服務在一個響應中添加了一個字段,那麼已有的消費方不應該受到影響

4.1.2 保證API的技術無關性

  • 保證微服務之間通信方式的技術無關性是非常重要的

4.1.3 使你的服務易於消費方使用

  • 消費方應該可以使用任何技術來實現,從另一方面來說,提供一個客戶端庫也可以簡化消費方的使用
  • 讓我們考慮一下,如何讓消費方簡便地使用美妙的新服務

4.1.4 隱藏內部實現細節

  • 所有傾向於暴露內部實現細節的技術都不應該被採用

4.2 為用戶創建接口

  • 創建客戶這個業務,乍一看似乎就是簡單的 CRUD 操作,但對於大多數系統來說並不止這些。添加新客戶可能會觸發一個新的流程,比如進行付賬設置、發送歡迎郵件等

4.3 共享數據庫

  • 目前為止,我和同事在業界所見到的最常見的集成形式就是數據庫集成。使用這種方式時,如果其他服務想要從一個服務獲取信息,可以直接訪問數據庫

4.4 同步與異步

  • 這兩種不同的通信模式有着各自的協作風格,即請求 / 響應或者基於事件
  • 哪些因素會影響對這兩種風格的選擇呢?一個重要的因素是這種風格能否很好地解決複雜問題,比如如何處理跨服務邊界的流程,而且這種流程有可能會運行很長時間

4.5 編排與協同

  • 編排方式的缺點是,客戶服務作為中心控制點承擔了太多職責,它會成為網狀結構的中心樞紐及很多邏輯的起點。我見過這個方法會導致少量的「上帝」服務,而與其打交道的那些服務通常都會淪為貧血的、基於 CRUD 的服務
  • 如果使用協同,可以僅僅從客戶服務中使用異步的方式觸發一個事件,該事件名可以叫作「客戶創建」。電子郵件服務、郵政服務及積分賬戶可以簡單地訂閱這些事件並且做相應處理
  • 構建與業務流程相匹配的監控系統。實際的監控活動是針對每個服務的,但最終需要把監控的結果映射到業務流程中
  • 使用異步方式有利於協同方案的實施,從而大大減少服務間的耦合,這恰恰就是我們為了能獨立發佈服務而追求的特性
  • 針對請求 / 響應方式,可以考慮兩種技術:RPC(Remote Procedure Call,遠程過程調用)和 REST(REpresentational State Transfer,表述性狀態轉移)

4.6 遠程過程調用

  • 遠程過程調用允許你進行一個本地調用,但事實上結果是由某個遠程服務器產生的。RPC 的種類繁多,其中一些依賴於接口定義(SOAP、Thrift、protocol buffers 等)

4.6.1 技術的耦合

  • 有一些 RPC 機制,如 Java RMI,與特定的平台緊密綁定,這對於服務端和客戶端的技術選型造成了一定限制

4.6.2 本地調用和遠程調用並不相同

  • RPC 的核心想法是隱藏遠程調用的複雜性。但是很多 RPC 的實現隱藏得有些過頭了,進而會造成一些問題

4.6.3 脆弱性

4.6.4 RPC很糟糕嗎


4.7 REST

  • REST 是受 Web 啟發而產生的一種架構風格。REST 風格包含了很多原則和限制,但是這裡我們僅僅專註於,如何在微服務的世界裏使用 REST 更好地解決集成問題。REST 是 RPC 的一種替代方案
  • 其中最重要的一點是資源的概念

4.7.1 REST和HTTP

  • HTTP 本身提供了很多功能,這些功能對於實現 REST 風格非常有用。比如說 HTTP 的動詞(如 GET、POST 和 PUT)就能夠很好地和資源一起使用

4.7.2 超媒體作為程序狀態的引擎

  • REST 引入的用來避免客戶端和服務端之間產生耦合的另一個原則是「HATEOAS」(Hypermedia As The Engine Of Application State,超媒體作為程序狀態的引擎。天哪,它真的需要一個縮寫嗎?)。這個概念很長也很有趣,所以讓我們詳細看一下
  • 超媒體的概念是:有一塊內容,該內容包含了指向其他內容的鏈接,而這些內容的格式可以不同(如文本、圖像、聲音等)。這個概念你應該很熟悉,因為你可以在任何一個網頁上看到超媒體控制形式的鏈接,當你點擊鏈接時可以看到相關的內容。HATEOAS 背後的想法是,客戶端應該與服務端通過那些指向其他資源的鏈接進行交互,而這些交互有可能造成狀態轉移。它不需要知道 Customer 在服務端的 URI,相反客戶端根據鏈接導航到它想要的東西

4.7.3 JSON、XML還是其他

  • 到目前為止我們看到的例子都是 XML 的,但事實上目前 JSON 更加流行

4.7.4 留心過多的約定

4.7.5 基於HTTP的REST的缺點

  • 有些 Web 框架無法很好地支持所有的 HTTP 動詞。性能上也可能會遇到問題

4.8 實現基於事件的異步協作方式

4.8.1 技術選擇

  • 微服務發佈事件機制和消費者接收事件機制
  • 這種系統通常具有較好的可伸縮性和彈性,但這麼做也是有代價的。它會增加開發流程的複雜度,因為你需要一個額外的系統(即消息代理)才能開發及測試服務。你也需要額外的機器和專業知識來保持這些基礎設施的正常運行。但一旦做好了,它會是實現松耦合、事件驅動架構的一種非常有效的方法。通常來說我很喜歡這種方式
  • 盡量讓中間件保持簡單,而把業務邏輯放在自己的服務中

4.8.2 異步架構的複雜性

  • 災難性故障轉移(catastrophic failover)的一個典型例子(http://martinfowler.com/bliki/CatastrophicFailover.html)
  • 代碼中的 bug 外,我們還忘了設置一個作業最大重試次數。我們也意識到需要有一種方式來查看甚至是重發這些有問題的消息。所以最後實現了一個消息醫院(或者叫死信隊列),所有失敗的消息都會被發送到這裡

4.9 服務即狀態機


4.10 響應式擴展

  • 響應式擴展(Reactive extensions,Rx)提供了一種機制,在此之上,你可以把多個調用的結果組裝起來並在此基礎上執行操作。調用本身可以是阻塞或者非阻塞的

4.11 微服務世界中的DRY和代碼重用的危險

  • 我的經驗是:在微服務內部不要違反 DRY,但在跨服務的情況下可以適當違反 DRY。服務之間引入大量的耦合會比重複代碼帶來更糟糕的問題

4.12 按引用訪問


4.13 版本管理

4.13.1 儘可能推遲

  • 減小破壞性修改影響的最好辦法就是盡量不要做這樣的修改
  • 另一個延遲破壞性修改的關鍵是鼓勵客戶端的正確行為,避免過早地將客戶端和服務端緊密綁定起來
  • 客戶端儘可能靈活地消費服務響應這一點符合 Postel 法則(也叫作魯棒性原則,https://tools.ietf.org/html/rfc761)。該法則認為,系統中的每個模塊都應該「寬進嚴出」,即對自己發送的東西要嚴格,對接收的東西則要寬容

4.13.2 及早發現破壞性修改

  • 強烈建議使用消費者驅動的契約來及早定位這些問題,第 7 章會對該技術做詳細的講解

4.13.3 使用語義化的版本管理

  • 語義化版本管理(http://semver.org/)就是一種能夠支持這種方式的規格說明。語義化版本管理的每一個版本號都遵循這樣的格式:MAJOR.MINOR.PATCH。其中 MAJOR 的改變意味着其中包含向後不兼容的修改;MINOR 的改變意味着有新功能的增加,但應該是向後兼容的;最後,PATCH 的改變代表對已有功能的缺陷修復

4.13.4 不同的接口共存

  • 我用過的一種比較成功的方法是,在同一個服務上使新接口和老接口同時存在
  • 這其實就是一個擴展 / 收縮模式的實例,它允許我們對破壞性修改進行平滑的過度。首先擴張服務的能力,對新老兩種方式都進行支持。然後等到老的消費者都採用了新的方式,再通過收縮 API 去掉舊的功能

4.13.5 同時使用多個版本的服務

  • 另一種經常被提起的版本管理的方法是,同時運行不同版本的服務,然後把老用戶路由到老版本的服務,而新用戶可以看到新版本的服務,
  • 我個人不太喜歡這個想法,也理解為什麼用 Netflix 的很少
  • 短期內同時使用兩個版本的服務是合理的,尤其是當你做藍綠部署或者金絲雀發佈時

4.14 用戶界面

4.14.1 走向數字化

4.14.2 約束

  • 儘管我們的核心服務可能是一樣的,但仍需要應對不同應用場景的約束

4.14.3 API組合

  • 與服務之間過多的交互對移動設備來說會有些吃力,而且對使用流量套餐的用戶來說也很不利!使用 API 入口(gateway)可以很好地緩解這一問題,在這種模式下多個底層的調用會被聚合成為一個調用,當然它也有一定的局限性,後面會做討論

4.14.4 UI片段的組合

  • 相比 UI 主動訪問所有的 API,然後再將狀態同步到 UI 控件,另一種選擇是讓服務直接暴露出一部分 UI,然後只需要簡單地把這些片段組合在一起就可以創建出整體 UI

4.14.5 為前端服務的後端

  • 對與後端交互比較頻繁的界面及需要給不同設備提供不同內容的界面來說,一個常見的解決方案是,使用服務端的聚合接口或 API 入口。該入口可以對多個後端調用進行編排,並為不同的設備提供定製化的內容,
  • 我個人比較喜歡的模式是,保證一個這樣的後端只為一個應用或者用戶界面服務
  • 這種模式有時也叫作 BFF(Backends For Frontends,為前端服務的後端)

4.14.6 一種混合方式

  • 一個組織會選擇基於片段組裝的方式來構建網站,但對於移動應用來說,BFF 可能是更好的方式。關鍵是要保持底層服務能力的內聚性

4.15 與第三方軟件集成

  • 不管怎樣,即使你所在的組織擁有很強的定製化軟件開發的能力,你還是需要外部組織提供的商業或者開源軟件產品。為什麼會這樣呢?
    • 你的組織對軟件的需求幾乎不可能完全由內部滿足
    • 也是最重要的一點是,這樣做非常低效!
  • 我的客戶經常糾結這樣的問題:「應該自己做,還是買?」一般來講,我和同事的建議是,對於一般規模的組織來說,如果某個軟件非常特殊,並且它是你的戰略性資產的話,那就自己構建;如果不是這麼特別的話,那就購買

4.16 小結

  • 最大程度地保證微服務之間 的低耦合:
    • 無論如何避免數據庫集成
    • 理解 REST 和 RPC 之間的取捨,但總是使用 REST 作為請求 / 響應模式的起點
    • 相比編排,優先選擇協同
    • 避免破壞性修改、理解 Postel 法則、使用容錯性讀取器
    • 將用戶界面視為一個組合層

  • 《REST 實戰》
  • 《企業集成模式》