聊聊微服務的通信模式

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務的目標是將應用程序儘量分解 \/ 解耦爲圍繞業務功能組織的一系列鬆散耦合服務。這些分佈式的微型單元共同滿足應用程序的目標。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將單個應用程序拆分爲多個微服務後,跨越多個服務的事務(讀取和寫入)就變得不可避免了。進而,"},{"type":"text","marks":[{"type":"strong"}],"text":"跨各個微服務邊界的通信——工作流管理——數據存儲機制"},{"type":"text","text":" 就成爲了挑戰。這一系統應符合被稱爲分佈式計算謬誤的準則。當跨多個服務(每個都有自己的業務邏輯和數據庫)處理事務時,數據庫系統承諾的 ACIDity 是無法保障的。CAP 定理 意味着你需要在一致性(C)和可用性(A)之間做出權衡,因爲分區容錯(P)在分佈式系統中是無法指望的。在這篇博客文章中,我們將探討針對這些挑戰和設計模式的解決方案。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"協調服務間通信"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對不同環境和目標的客戶和服務可以通過不同的機制來通信。通信可以是同步的或異步的,具體取決於協議。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"同步通信——請求響應方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在同步通信中,需要一個預定義的源服務地址,指明請求要發送到何處,並且 "},{"type":"text","marks":[{"type":"strong"}],"text":"兩邊的服務"},{"type":"text","text":"(調用方和被調用方)都應處於啓動和運行狀態。儘管協議可能是同步的,但 I\/O 操作可以是異步的,其中客戶端不必等待響應。這是 "},{"type":"text","marks":[{"type":"strong"}],"text":"I\/O 和協議"},{"type":"text","text":" 之間的區別。Web API 常見的通用請求 - 響應方法包括 REST、GraphQL 和 gRPC。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"異步通信"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在異步通信的情況下,調用方不必有被調用方的具體地址。這樣就可以相對容易地一次處理多個消費者(因爲服務可能會增加消費者數量)。此外,如果接收服務關閉,消息就會進入隊列,然後在接收服務打開時繼續處理。從 "},{"type":"text","marks":[{"type":"strong"}],"text":"鬆散耦合、多服務通信以及應對部分服務器故障"},{"type":"text","text":" 的角度來看,這尤其重要。正是這些決定性的因素讓 "},{"type":"text","marks":[{"type":"strong"}],"text":"微服務傾向於異步通信"},{"type":"text","text":"。諸如 MQTT、STOMP、AMQP 之類的異步協議由 Apache Kafka Stream、RabbitMQ 之類的平臺處理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解何時何地使用同步模型與異步模型,是設計高效微服務通信機制時的基礎要素。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"消息與事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在異步通信中,常見的機制是消息傳遞和事件流。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"消息"},{"type":"text","text":" 是發送到特定目的地的數據項目,它封裝了 "},{"type":"text","marks":[{"type":"strong"}],"text":"意圖 \/ 動作"},{"type":"text","text":"(需要發生的事情),並通過消息傳遞之類的渠道分發。隊列負責存儲消息,直到它們得到處理和刪除。在消息驅動的系統中,可尋址的收件人等待消息到達並做出響應,否則將處於休眠狀態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件封裝了狀態的變化(發生了什麼),而事件偵聽器會附加到事件源上,以便在事件發出時調用它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"域事件:與應用程序生成的業務域相關的事件(下圖中的 OrderRequested、CreditReserved、InventoryReserved)。這些事件是事件源關注的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更改事件:從數據庫生成的事件,指示狀態轉換。這些事件是更改數據捕獲所關心的。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件 streamer 是耐用的、持久的、容錯的,不受消費者干預。在這種情況下,處理器是 dumb 類型(從某種意義上說,它僅充當消息路由器),並且客戶端 \/ 服務擁有以域爲中心的,負責 "},{"type":"text","marks":[{"type":"strong"}],"text":"轉儲處理器與活躍客戶端"},{"type":"text","text":" 的邏輯。這樣就避免了複雜的集成平臺,例如傳統 SOA 設計中使用的 ESB。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/bd\/bd3496880f8284c70cd953991c55dc12.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:微服務設計中的事件"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"微服務原理——智能消費者與啞管道"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務社區提出了智能端點和啞管道的理念。Martin Fowler 是他稱爲微服務通信的智能端點和啞管道的擁護者。統治 SOA 領域的 ESB 存在與複雜性、成本和故障排除相關的諸多問題。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步通信協議"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MQTT——消息隊列遙測傳輸(MQTT)是一項基於發佈 \/ 訂閱的輕量級消息傳遞協議 ISO 標準,已在物聯網中廣泛使用。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AMQP——高級消息隊列協議(AMQP)是一項開放標準應用程序層協議,用於面向消息的中間件。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"STOMP——簡單文本定向消息傳遞協議(STOMP)是 HTTP 上基於文本的協議,用於在服務之間交換數據。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"通用消息 \/ 流平臺"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ActiveMQ"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kafka"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RabbitMQ"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis Streams"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"評估標準的一些常見基準包括可用性、持久性 \/ 耐用性、耐用性、推 \/ 拉模型、可伸縮性和消費者能力。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"微服務設計模式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務建立在獨立和自治服務、可伸縮性、低耦合 + 高內聚和容錯性等原則上。這些原則會帶來許多挑戰,包括複雜的管理和配置需求。微服務設計模式的目的是在給定的上下文中描述問題的可重用解決方案。我們將探討這些模式如何應對挑戰,以提供經過驗證的解決方案來打造更高效的微服務架構。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Saga 模式——跨多個服務維護原子性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單個事務可能會跨越多個服務。例如,在電子商務應用程序中,新訂單(與訂單服務鏈接)不應超過客戶信用額度(與客戶服務鏈接),並且貨品(與庫存服務鏈接)應處於可用狀態。這個事務根本不能使用本地 ACID 事務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個 saga 是一系列本地事務,這些事務可更新各個服務併發佈一個消息 \/ 事件以觸發下一個本地事務。有任何本地事務失敗的情況下,saga 會執行一系列 "},{"type":"text","marks":[{"type":"strong"}],"text":"補償事務"},{"type":"text","text":",以回退先前的本地事務所做的更改,從而保持 "},{"type":"text","marks":[{"type":"strong"}],"text":"原子性"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於編舞(Choreography)的 saga——參與者在沒有中心化控制點的情況下交換 "},{"type":"text","marks":[{"type":"strong"}],"text":"事件"},{"type":"text","text":"。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於編排的 saga——一箇中心化控制器告訴 saga 參與者要執行哪些本地事務。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這兩種模式之間具體選擇哪一種,取決於工作流程的複雜性、參與者數量、耦合水平以及其他因素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"兩階段提交"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與 saga 類似,一個事務會分爲兩個階段:準備和提交階段。在準備階段,要求所有參與者準備數據;在提交階段則進行實際更改。但是,由於同步存在副作用和性能問題,因此這種模式在微服務架構中被認爲是行不通的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"事件源——面向狀態的持久性策略的替代方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"保持數據的傳統方法是更新已有數據來保持實體狀態的最新版本。假設,如果我們必須更改用戶實體的名稱,我們將使用新的用戶名來改變當前狀態。如果我們需要在任何時間點或一段時間上重建狀態該怎麼辦?在這種情況下,我們需要考慮這種持久性策略的替代方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與這種面向狀態的持久性相反,事件源將每個狀態突變存儲爲一個單獨事件,而應用程序狀態則存儲爲 "},{"type":"text","marks":[{"type":"strong"}],"text":"不可變"},{"type":"text","text":" 事件的一個序列 \/ 日誌,而不是修改數據。通過有選擇地重播事件,我們可以隨時瞭解應用程序的狀態。應用程序在稱爲事件存儲的僅附加事件日誌中保持數據。一個著名的例子是事務數據庫系統的事務日誌。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件源基於三個服務層:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"命令"},{"type":"text","text":":由一個命令處理器處理的狀態更改請求。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"事件"},{"type":"text","text":":狀態更改的不可變表示。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"聚合"},{"type":"text","text":":域模型當前狀態的聚合表示。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件源模式有很多優勢,包括提供準確的審覈日誌、重建任意時間點的狀態、方便的時間查詢、時間旅行、高性能和可伸縮性等。Netflix 通過事件源解決了離線下載功能需求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"CQRS——命令查詢職責隔離"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們將 CRUD 操作設計爲可以用兩種獨立的讀寫模型來處理,會有什麼後果呢?它顯然增加了系統的複雜性,但收益是什麼?何時需要它?這種隔離增加了另一層可伸縮性、性能和靈活性,從而在處理複雜的域模型時實現精細的讀寫優化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CQRS 將在應用程序中進行更改的模型 \/ 對象與讀取應用程序數據的模型 \/ 對象清楚地區分開。命令只是方法,其唯一目的是執行動作(創建、更新、刪除),並且可以被接受或拒絕,而不會暴露系統狀態。查詢是無需修改即可讀取系統狀態的方法。更進一步,我們可以引入一種保持同步的機制來拆分數據存儲的寫入部分和讀取部分(可以由多個數據庫管理)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/5d\/5da3f5b236ac7e79996613fb030b516a.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:具有相同和不同數據存儲的 CQRS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"事件源和 CQRS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些通常被稱爲補充模式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“沒有事件源的情況下也可以使用 CQRS,但有了事件源,你就必須使用 CQRS”——GregYoung,CQRS and Event Sourcing——Code on the Beach 2014。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如前所述,事件存儲由不可變事件的一個序列組成。通常,業務需求想要 "},{"type":"text","marks":[{"type":"strong"}],"text":"執行復雜的查詢,而這些查詢無法由一個聚合響應"},{"type":"text","text":"。每次都重播事件序列需要花費大量計算資源(並且在龐大的數據集中很難做到)。在這種情況下,隔離就會是有益的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下圖中,更新事件存儲的命令將發佈事件。查詢服務消費了更改日誌事件,併爲將來的查詢建立一個預測。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/bc\/bc2914caa63f9489e65ac05242e908e5.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:一個服務中的事件源和 CQRS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"事務發件箱模式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在某些情況下,我們需要在數據庫中進行更新,經常還要在外部系統上調用另一個動作。例如,在電子商務應用程序中,我們需要保存訂單並向客戶發送電子郵件。如果其中一個事務失敗,就可能導致系統不一致。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這種情況下,"},{"type":"text","marks":[{"type":"strong"}],"text":"發件箱(outbox)"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"strong"}],"text":"消息中繼(message relay)"},{"type":"text","text":" 可以共同作用,可靠地保持狀態並調用其他動作。“"},{"type":"text","marks":[{"type":"strong"}],"text":"發件箱"},{"type":"text","text":"”表位於服務的數據庫中。與主要更改(例如在訂單表中創建訂單)一起,代表事件(orderPlaced)的記錄也被引入同一數據庫事務中的發件箱表中。在非關係數據庫中,這通常是通過將事件存儲在文檔內部來實現的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,消息中繼讀取發件箱表,並將消息轉發到相應的目的地。消息分派過程可以是輪詢發佈者(輪詢發件箱表)或事務日誌跟蹤(跟蹤數據庫提交日誌)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/53\/53ca7d450b64fefe5cff4b6b8daf6094.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:事務發件箱模式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"更改數據捕獲(CDC)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用程序狀態保留在數據庫中。更改數據捕獲(Change Data Capture)跟蹤一個源數據庫中的更改,並將這些更改轉發到預定目的地,以和相同的增量更改同步。CDC 可以是基於日誌的(事務數據庫將所有更改存儲在事務日誌中)或基於查詢的(定期用查詢檢查源數據庫,因爲事務日誌可能在 Teradata 這樣的數據庫中不可用)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖顯示了基於日誌的 CDC,用於捕獲發件箱表中的新條目(使用 Debezium connector for Postgres)並將它們流式傳輸到 Apache Kafka。事件捕獲發生時的開銷非常低,幾乎是實時的,並且這些事件是訂閱的目的地服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/0d\/0d773d7eefda9d2ce5f0465bc5c95fb9.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:事務發件箱與 CDC 使用 ApacheKafka"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"微服務設計注意事項"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們將簡要介紹設計微服務時需要的其他一些思想 \/ 原理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"冪等事務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"冪等事務指的是發出多個相同請求的事務,這些事務具有與發出單個請求相同的效果。在 REST API 中,GET 方法是冪等的(可以反覆調用,結果與單次處理的結果是確定一致的),而 POST 方法不是冪等的(每個請求都會添加新項目)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分佈式系統的上下文中,你不能實現消息的精確一次傳遞。消息代理(例如 Apache Kafka 或 RabbitMQ)實現了"},{"type":"text","marks":[{"type":"strong"}],"text":"至少一次傳遞"},{"type":"text","text":",從而爲同一事務帶來了多次調用的可能性。因此,在分佈式系統中,消費者需要是冪等的。如果消費者不是冪等的,則多次調用可能導致錯誤和不一致。、"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Airbnb 將一個名爲“"},{"type":"text","marks":[{"type":"strong"}],"text":"Orpheus"},{"type":"text","text":"”的 通用冪等庫 實現到了多個支付服務中,其中一個 "},{"type":"text","marks":[{"type":"strong"}],"text":"冪等鍵"},{"type":"text","text":" 被傳遞到框架中,表示一個單獨的冪等請求。Paypal 使用 MsgSubId(消息提交 ID)在 API 中實現了冪等性,而 Google Service Payment 使用 request ID 實現了冪等性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/20\/200795501e1770fc43396593f3a86663.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:“火車目的地”標誌控制中的“開 \/ 關”按鈕。按下“開”按鈕是冪等的,因爲無論執行一次還是多次都具有相同的效果。同樣,按下“關”也是冪等的。(來源:維基百科)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"最終一致性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分佈式系統中,一致性定義了對一個節點 \/ 服務的更新是否以及如何傳播到所有服務。最終一致性也稱爲 "},{"type":"text","marks":[{"type":"strong"}],"text":"樂觀複製"},{"type":"text","text":",它只是一個聲明,表明將一臺計算機上所做的更改傳播到所有其他副本上的操作存在不受約束的延遲。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"網絡分區是分佈式系統需要面對的一個現實,那就是網絡可能會出故障。由於分區容限(P)是不可避免的,因此 CAP 定理要求你在一致性和可用性之間做出權衡。如果選擇可用性,就不能有很強的一致性,但你仍然可以在系統中提供最終一致性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"許多業務系統對數據不一致的容忍度比預期的要高。BASE(基本可用、軟狀態和最終一致性)系統優於 ACID 系統。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“對於分佈式系統而言,保持強一致性非常困難,這意味着所有人都必須管理最終一致性。”——Martin Fowler"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/wechat\/images\/c1\/c103d69d2ad317d1bc8fa74b9bf4ec4c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"圖:分佈式系統中的最終一致性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分佈式跟蹤"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在微服務中,與(可能跨越多個服務的)請求相關聯的元數據在很多場景中都能發揮作用:監視、日誌聚合、故障排除、延遲和性能優化、服務依賴分析,以及分佈式上下文傳播。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分佈式跟蹤是從頭到尾捕獲請求的 "},{"type":"text","marks":[{"type":"strong"}],"text":"元數據"},{"type":"text","text":" 的過程,以將日誌記錄開銷保持在最低水平。一個 "},{"type":"text","marks":[{"type":"strong"}],"text":"唯一事務 ID"},{"type":"text","text":" 被分配給外部請求,並通過分佈式拓撲中各個事務的調用鏈傳遞,還包含在所有消息中(包括時間戳和元數據)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用數據庫票證服務器(由 Flickr 使用)、UUID 或 Twitter SnowFlake 生成唯一標識符。常見的分佈式跟蹤工具包括 OpenTracing、Jaeger、Zipkin 和 AppDash。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"服務網格"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務中的服務網格是處理進程間通信的可配置網絡基礎設施層。它和通常稱爲 Sidecar 代理或 Sidecar 網關之的東西很像。它提供了以下功能:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務發現"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"健康檢查"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安全性"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Envoy 是專爲雲原生應用程序設計的流行開源代理。Istio 是一個開放平臺,用於連接、管理和保護 Kubernetes 社區中流行的微服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/medium.com\/dev-genius\/microservice-architecture-communication-design-patterns-70b37beec294","title":"","type":null},"content":[{"type":"text","text":"https:\/\/medium.com\/dev-genius\/microservice-architecture-communication-design-patterns-70b37beec294"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章