使用Kafka Streams構建事件源系統的經驗

banq發表於2018-09-05
在基輔召開的JEEConf會議上,Amitay Horwitz描述了他和他的團隊如何實施事件溯源的發票系統,他們在生產2年半後遇到的挑戰以及他們如何使用Kafka Streams實施新設計。

Wix的軟體工程師Horwitz 於2015年開始與他的團隊一起開展新的發票服務,目的是幫助他們的客戶管理發票並線上接收付款。在設計新服務時,他們希望建立一個小而簡單的庫,該庫非侵入式,維護資料完整性,並且能夠輕鬆新增自定義檢視。為了實現所有目標,他們決定使用事件源架構來實現服務。

他們也遇到了問題,客戶有時無法看到新建立的發票,因為從寫入到讀取是最終一致性。

但他們最大的問題是重建檢視。確保在沒有事件進入的情況下觸發重建,事實證明它比預期的更復雜,特別是在具有來自各種伺服器的事件的分散式環境中。這些問題使Horwitz尋求替代架構,同時保持事件溯源的好處。

Horwitz將Kafka描述為一個複製的,容錯的,分散式附加日誌。它通常用於pub-sub或佇列,但他指出它可以做更多。Kafka中的基本結構是一個分割槽的主題,一個邏輯佇列。生產者將根據訊息中的key將訊息推送到每個分割槽,然後消費者可以使用這些訊息。對於事件源系統至關重要的兩個重要特性是在單個分割槽內維護訊息之間的排序,並且訊息即使在消耗之後也可以儲存

Kafka Streams將流處理帶給了Kafka。它有兩個主要的抽象:

1. Horwitz認為流中的資料流是一種無限有序和可重放的不可變資料序列,這使得它對於事件源系統來說很有趣。保證了順序。

2. 能在靜態資料中看到Table,表Table儲存聚合資料的某個時間點狀態檢視,該檢視在接收到新訊息時更新。

在使用Kafka的發票服務的新設計中,有一個快照狀態儲存,用於儲存每個聚合的當前狀態,從命令流接收命令後,命令處理程式Handler會根據聚合ID從快照狀態儲存中讀取相應聚合的當前狀態;然後,該處理程式Handler確定命令是成功被執行還是失敗了,並透過結果流返回結果;如果命令執行成功,則會建立事件並將其傳送到事件儲存庫;接下來,讀取這邊系統查詢有新事件的流,並將狀態儲存中的聚合更新為其新狀態(banq注:注意更新狀態在讀取這邊,不是寫入那邊),他指出,命令處理程式Hanlder邏輯可以用非常簡潔和宣告的方式編寫,在他的示例中只有60行Scala程式碼。

在新的架構中,Kafka位於中心,微服務與Kafka通訊,彼此之間也透過Kafka進行通訊。他們還可以在建立分析報告時將資訊推送到Kafka或提取資訊,總之,Horwitz指出,新設計給了他們幾個優勢:

1. 一個簡單的宣告式系統
2. 最終的一致性現在已被接受並優雅地處理
3. 新增或更改檢視很容易
4. 使用Kafka提高了可擴充套件性和容錯能力

新設計仍處於評估階段,儘管他們在生產中大量使用卡夫卡。他指出,有人聲稱Kafka不適合CQRS或事件來源系統,但他認為,如果能進行權衡,Kafka可以被利用。如果使用不同的客戶端屬性儲存頁面檢視事件,就可以根據該事件資訊輕鬆建立聚合,這是一種事件溯源形式,他認為Kafka非常適合。

透過使用聚合的標識作為Kafka的分割槽鍵key,同一聚合的所有命令將最終位於命令主題的同一分割槽中,並將在單個執行緒中(單寫)按順序處理。這樣,在前一個生成所有下游事件之前不會處理任何命令,並且Horwitz指出這將建立強大的一致性保證。

https://www.infoq.com/news/2018/07/event-sourcing-kafka-streams

https://www.youtube.com/watch?v=b17l7LvrTco

相關文章