Java反應式事件溯源之第5部分:事件儲存
選擇事件儲存資料庫需要大量研究。這可能是一個單獨的部落格系列的主題(可能在將來),所以我的計劃是從不同的角度來解決它。不要試圖找到最佳解決方案(因為恕我直言不存在)。相反,嘗試找到一個最佳的折衷方案併為改變做好準備。
使用Akka Persistence有一個巨大的優勢,即永續性只是一個外掛。我們可以將此庫與任何能夠履行AsyncWriteJournal合同的事件儲存一起使用。我們只需要實現 4 種方法來使用我們的自定義事件儲存解決方案:
@Override public Future<Iterable<Optional<Exception>>> doAsyncWriteMessages(Iterable<AtomicWrite> iterable) {...} @Override public Future<Void> doAsyncDeleteMessagesTo(String s, long l) {...} @Override public Future<Void> doAsyncReplayMessages(String s, long l, long l1, long l2, Consumer<PersistentRepr> consumer) {...} @Override public Future<Long> doAsyncReadHighestSequenceNr(String s, long l) {...} |
SnapshotStore和ReadJournal的讀取模型投影的情況類似。
我並不是說實現自定義 Akka Persistence 外掛是一項微不足道的任務。這將是一個相當大的挑戰,但至少是可能的(這是我同事的例子)。我們在未來不受今天在當前背景下做出的選擇的限制。此外,我們可以推遲一些困難的決定,例如使用分散式事件儲存(例如 Cassandra)還是使用單個主機(例如 PostgreSQL、MariaDB)。
對於初學者,有可用的外掛,例如:
- akka-persistence-jdbc
- akka-persistence-cassandra
- akka-persistence-couchbase
- akka-persistence-spanner
- akka-persistence-dynamodb
JDBC
經典的關聯式資料庫適用於很多專案。我想大多數開發人員對這種資料庫都有一些經驗。要為我們的事件源行為啟用Akka Persistence JDBC 外掛,您所要做的就是新增適當的依賴項和一些配置:
akka{ persistence.journal.plugin = "jdbc-journal" } jdbc-journal { slick = { profile = "slick.jdbc.PostgresProfile$" db { host = "localhost" port = "5432" dbName = "postgres" url = "jdbc:postgresql://"${slick-akka.db.host}":"${slick-akka.db.port}"/"${slick-akka.db.dbName}"?reWriteBatchedInserts=true" user = "admin" password = "admin" driver = "org.postgresql.Driver" } } } |
在我們執行docker-compose -f development/docker-compose-jdbc.yml起來之後,我們可以啟動我們的應用程式,提出一些請求,並驗證我們的關係型事件儲存在event_journal表中包含一些事件。不要對event_payload列感到驚訝,因為我們的事件是以二進位制blob的形式儲存的。對於原型設計,我們使用的是Java序列化機制,當然這對於生產部署來說是一個非常糟糕的主意,應該改變。在這一部分,我們將不關注序列化本身(這需要一個單獨的帖子),但如果你想閱讀更多關於可能的選擇,只需閱讀我關於事件源的序列化策略的帖子。
這樣一個解決方案的反應性如何?與事件儲存進行通訊的JDBC驅動是一個阻塞式的。的確,在寫這篇文章的時候,R2DBC(反應式JDBC)驅動還不被Akka Persistence支援。我希望這在未來會有所改變。幸運的是,JDBC驅動被一個Slick庫包裹著,它給了我們一個相當不錯的、可管理的(一個獨立的執行緒池)反應式門面。不要忘了,Actor不會因為與底層事件儲存的互動而被阻斷。
Cassandra
在某種程度上,單個主機資料庫是不夠的。垂直縮放將達到其極限並處理更多負載,我們將需要切換到分散式的東西。雖然,並非所有分散式資料庫都會提高吞吐量。使用leader-follower 架構(例如MongoDB),我們仍然會受到單個主機(leader)的限制。我們應該關注像 Apache Cassandra 這樣的無領導者解決方案。從寫的角度來看,Cassandra 是事件儲存的絕配,我們會得到:
- 分割槽(資料分佈在叢集中的所有節點上),
- 複製(如果節點發生故障,我們可以使用副本),
- 針對寫入進行了優化(吞吐量非常好),
- 近線性水平縮放。
如果你曾經使用過 Cassandra,你應該知道模式建模是這個資料庫的關鍵。Akka 永續性 Cassandra外掛意識到了這一點,用於儲存事件的表模式就像一個魅力。分割槽不會太小,但也不會太大(實際上,您可以配置單個分割槽應該有多少事件)。使用這樣的模式,讀取給定聚合的事件將非常有效(對於恢復階段很重要)。通訊的反應式驅動器將非常適合我們的設計。任何問題?當然 :) 在生產環境中維護分散式事件儲存與單個主機資料庫完全不同。這將需要大量的 DevOps 能力和知識(或者在託管解決方案的情況下需要資金)。此外,請注意,從所有聚合中讀取所有事件可能會帶來一些挑戰。我想在某種程度上,我們根本沒有選擇。如果我們想進一步擴充套件,
好訊息是我們不必在實現中進行任何更改即可使用 Cassandra。和以前一樣,我們只需要一些依賴項和配置:
akka { persistence.journal.plugin = "akka.persistence.cassandra.journal" } akka.persistence.cassandra.journal { keyspace-autocreate = true //not recommended for production tables-autocreate = true //not recommended for production } |
使用part_5原始碼中的 Cassandra 外掛執行我們的應用程式,我們需要-Dconfig.resource=/application-cassandra.conf在 Intellij 啟動配置中新增 VM 選項或直接從命令列執行它:
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="--enable-preview -Dconfig.resource=/application-cassandra.conf" |
概括
我希望你能感受到 Akka Persistence 的這一無可置疑的優勢,我們可以使用記憶體中的事件儲存進行原型設計,然後在生產環境中切換到 JDBC 事件儲存,一旦你遇到一些重大負載,就切換到像 Apache Cassandra 這樣的分散式事件儲存. 對我來說,這改變了遊戲規則。我可以把非常困難的決定推遲到最後一刻。我還應該提到,我們可以為每個聚合型別配置事件儲存。對於會產生數千個事件的長期聚合,例如交易所市場、物聯網,我可以使用 Apache Cassandra,但對於具有少量事件的聚合,我可以使用 JDBC 外掛。
在使用完全不同的資料庫時,肯定會有一些特定的陷阱和陷阱。事件遷移將需要一些工作,這將是一個過程。關係事件儲存上的模式與 Apache Cassandra(或任何其他分散式資料庫)上的模式完全不同,但至少是可能的。我們將來不會被阻止,從一個資料庫到另一個資料庫的神話般的切換可能是真正的交易。這是可能的,因為使用事件溯源,我們不必太關心資料之間的關係。我們需要儲存一個事件流並讀取事件流,這可以在許多資料庫引擎上進行模擬。使用 Actor 模型,我們可以使用(分散式)單寫原則,並安全地使用不提供 ACID 事務保證的資料庫。
如果您想將 Akka 用於 CRUD 功能,還有一個具有持久狀態永續性的選項。
像往常一樣 - 檢視part_5原始碼並使用該應用程式。
相關文章
- Java反應式事件溯源之第 2 部分:Actor 模型Java事件模型
- Java反應式事件溯源之第 4 部分:控制器Java事件
- Java反應式事件溯源之第3部分:服務Java事件
- Java反應式事件溯源:領域Java事件
- 事件消費者之 Saga - 事件溯源事件
- 事件消費者之 Reactor - 事件溯源事件React
- 事件消費者之 Projector - 事件溯源事件Project
- 事件流與事件溯源事件
- 事件協作和事件溯源事件
- .NET分散式Orleans - 6 - 事件溯源分散式事件
- PHP 事件溯源PHP事件
- lakeFS:實現類似於Git或事件溯源ES的物件儲存功能Git事件物件
- 剖玄析微聚合 - 事件溯源事件
- 事件溯源全指南 - Arkwrite事件
- 事件溯源超越關聯式資料庫 - confluent事件資料庫
- 如何在Java後端中實現事件驅動架構:從事件匯流排到事件溯源Java後端事件架構
- Linux伺服器應急事件溯源報告Linux伺服器事件
- 函式化事件溯源的決策者模式 - thinkbeforecoding函式事件模式
- 使用Kafka實現事件溯源Kafka事件
- Rust中的事件溯源 - ariseyhunRust事件
- 物件儲存服務的事件通知特性物件事件
- 事件溯源將顛覆關聯式資料庫! - Remy事件資料庫REM
- 使用EventStoreDB實現事件溯源的Java開源專案事件Java
- Chronicle事件溯源的最佳實踐事件
- Python的事件溯源開源庫Python事件
- Spring Boot和EventStoreDB事件溯源案例Spring Boot事件
- Occcurrent:JVM事件溯源工具庫包JVM事件
- 事件溯源:是來自事件的狀態與作為狀態的事件? - verraes事件
- HomeAway分享雲端事件溯源經驗事件
- 從入門到放棄 - 事件溯源事件
- 從增刪改查到事件溯源 - PHP事件PHP
- .NET的事件溯源構建庫:Eventuous事件
- 第5章 虛擬儲存器
- 如何讓客戶方便地使用事件溯源?事件溯源有什麼好處?- daryush_d事件
- Java進階09 事件響應Java事件
- 無伺服器與事件溯源結合的演示案例:將事件溯源作為Azure函式的資料持久化機制的庫伺服器事件函式持久化
- 使用mousedown事件使用右鍵觸發無反應事件
- 事件溯源與流水賬的結賬模式事件模式