在一個由許多小型服務組成的系統中保持數據一致性是困難的,因為它們分散在各處。以下是一些常見問題以及如何處理它們的方法:當服務發送消息時,同時更新數據庫和發送消息是棘手的問題。
在微服務中發出事件時,我們必須解決如何以事務方式更新數據庫并發出事件的問題。
處理這個問題的簡單方法是使用事務性 Outbox 模式。
當我們必須同時更新兩個不同的系統時,就會出現雙寫問題。例如,如果我們需要在 Apache Kafka 和數據庫中記錄事件。由于這些系統沒有連接,我們無法一次性更新它們。我們必須找到一種方法來確保兩者同時更新,或者兩者都不更新。這就是事務性 Outbox 模式發揮作用的地方。
如果我們的數據庫支持事務性更新,我們可以使用它來解決雙寫問題。我們將事務邏輯移到數據庫中,而不是嘗試同時更新數據庫和 Kafka。每當我們更新數據庫時,我們也在同一個事務中更新一個 Outbox 表。可以將 Outbox 想象成一個郵箱,我們將需要發送的信件放在其中。然后,我們等待郵遞員收集這些信件并將其送到郵局。在我們的情況下,這些信件代表我們想要發送到 Kafka 的事件,而 Kafka 則充當郵局。但是,我們仍然需要某種東西扮演郵遞員的角色。
要從 Outbox 表中發出事件到 Apache Kafka,我們可以使用一個單獨的進程來異步監視該表。每當它檢測到事件時,就可以將其發送到 Kafka。
雙寫問題
一旦事件成功傳遞,它就可以從 Outbox 表中刪除。該進程通常是在原始微服務中的另一個線程中編寫的;但是,它也可以作為完全獨立的應用程序運行。根據您使用的數據庫,您可能可以使用 Kafka 連接器(例如 Postgres-Kafka 連接器)或更改數據捕獲(CDC)系統(例如 Debezium)來監視表并發送事件。
Kafka 連接器或更改數據捕獲(CDC)
Outbox 事務模式避免了雙寫問題。原因是狀態和 Outbox 表將始終以事務方式更新。如果由于某種原因狀態未能更新,則事件不會寫入 Outbox;這意味著我們可以保證 Outbox 中的數據與數據庫中的數據完全同步。然后,負責將事件傳遞到 Kafka 的獨立進程確保 Outbox 表和 Kafka 保持同步。這使我們能夠保證每個數據庫操作都會在 Kafka 中有一個相應的事件,盡管會有一點延遲。
缺點:當傳遞過程向 Kafka 發出事件時,可能會出現失敗或超時。在這種情況下,為了確保 Kafka 收到數據,我們必須重試。這些重試可能導致重復的消息;因此,我們向 Kafka 的傳遞保證是至少一次的。我們保證 Outbox 中的每條消息最終都會到達 Kafka,但可能會重復到達。因此,我們需要確保下游系統準備好處理任何重復消息。
在分布式系統中,至少一次的保證是常見的,因此,即使不涉及雙寫問題,實現去重邏輯也是一個良好的做法。例如,接收方在處理 Kafka 消息時可能會失敗,并且當它重新啟動時可能會再次收到相同的消息。
Outbox 模式中的挑戰
我們必須準備好處理這些情況。這可能會導致大量的流量。頻繁的更新意味著數據庫可能會始終將表保存在內存中,占用大量資源。與此同時,一些數據庫在處理刪除時效率不高。它們可能在幕后使用墓碑,并且隨著頻繁的插入和刪除發生,這些墓碑可能會累積,這會導致資源使用量大增,并在我們的表中引起爭用。如果數據庫無法處理此類流量,可能會減慢我們的應用程序,因為請記住,每個寫入都將觸及該 Outbox 表。為了解決這些問題,我們可能需要進行調整,例如將記錄而不是刪除它們,或者調整數據庫管理墓碑的方式。保留事件可能會帶來長期的好處,因此刪除可能并非絕對必要。一些數據庫專門設計用于處理這種類型的流量。
如果您的系統滿足事務性 Outbox 模式的要求,那么它可以是解決雙寫問題的一種簡單有效的方法。與其他選項(例如事件溯源或監聽自己模式)相比,這種方法采用事件優先的方法,使用 Kafka 實時通知微服務變更,保持系統一致性。但是,諸如訂單履行之類的組件可能需要編排,無法運行。
本文鏈接:http://www.tebozhan.com/showinfo-26-86996-0.html微服務架構中的挑戰及應對方式:Outbox 模式
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
下一篇: 如何用C++實現簡單的內存池