使用Axon重播投射事件 - codecentric AG Blog

banq發表於2019-12-28

事件儲存是任何事件源應用程式的核心。它包含系統生命週期中發生的每個事件。這些事件包含應用程式中的每個狀態更改。EventSourcing通常與命令查詢責任隔離(CQRS)結合使用。對於Axon而言,這意味著可以在投影中實現單獨的讀取面。 通常,事件在兩個主要位置影響了軟體的當前狀態:聚合和投影。 

事件處理器

如果您熟悉Axon,則可能知道處理器有兩種型別:訂閱事件處理器和跟蹤事件處理器。這些處理器之間的主要區別在於,訂閱事件處理器將自己訂閱事件源,並由釋出機制管理的執行緒呼叫。另一方面,跟蹤事件處理器使用它自己管理的執行緒從源中提取訊息。

投射

我們軟體的當前狀態是由聚合執行這些事件的結果。投影是基於這些事件的特定檢視,每個檢視都有其自己的目的。例如,如果您向新銀行註冊,則將在“ CustomerRegisteredEvent”行中發出事件。您可以想象一些投射對此事件感興趣。比如可以跟蹤已註冊到銀行的客戶的所有個人資訊的計劃,或者可以跟蹤客戶在填寫其所需的所有資訊方面所取得的進展的計劃。  

但是,隨著時間的流逝,需求將發生變化,您將看到您希望更新結構和內容上的投射。這就是Axon重播的地方。重播是Axon框架支援的CQRS和EventSourcing的核心概念。

僅當您使用跟蹤事件處理器時,才可以在Axon中重播。 

一旦指定了要重播的事件,Axon將:

  • 在重置之前,使用提供的事件和令牌的值建立重播令牌
  • 更新令牌儲存中的令牌
  • 從該點開啟事件流
  • 閱讀事件
  • 在跟蹤事件處理器上以工作單元處理事件

Spring Rest API

如何發起重播?有多種方法可以做到這一點。從Axon 4開始,可以在Axon Server中重播,但這超出了本部落格文章的範圍。另一種方法是在某個時間點重置跟蹤事件處理器。此解決方案的一個警告是,您必須在重置之前手動停止跟蹤事件處理器。第三種選擇是,您可以手動更新令牌儲存中的跟蹤令牌以重新處理過去的事件,最後,我們的首選解決方案是使用Axon提供的Replay API。 

我們在Spring中利用此Replay API建立了安全的Rest API。我們唯一需要提供的資訊是我們要重播的位置以及跟蹤事件處理器的名稱。在重置處理器之前,我們檢查了處理器是否實際上在此例項上執行,如果是,則通過程式碼將其關閉。最終,Axon建立了一個重設令牌,該令牌包含重置時令牌的值,並將其插入令牌儲存中。當我們重播所有事件時,Axon會將重播令牌轉換回跟蹤令牌,並從中斷處繼續執行。

為了讓我們對TrackingEventProcessorService的外觀有所瞭解:

@Service
@Slf4j
public class TrackingEventProcessorService {
 
  private final EventProcessingConfiguration eventProcessingConfiguration;
 
  public TrackingEventProcessorService(
        EventProcessingConfiguration eventProcessingConfiguration
  ) {
     this.eventProcessingConfiguration = eventProcessingConfiguration;
  }
 
  private TrackingEventProcessor getTrackingEventProcessor(String name) {
     return this.eventProcessingConfiguration
           .eventProcessor(name, TrackingEventProcessor.class)
           .orElseThrow(TrackingEventProcessorNotFoundException::new);
  }
 
  public boolean replay(String trackingEventProcessorName, Long index) {
     TrackingEventProcessor trackingEventProcessor = this.getTrackingEventProcessor(trackingEventProcessorName);
     if (!trackingEventProcessor.isRunning()) {
        this.logger
              .warn(
                    "Tracking event processor {} is not running in current instance or not running at all",
                    trackingEventProcessorName
              );
        return false;
     }
 
     trackingEventProcessor.shutDown();
 
     try {
        trackingEventProcessor.resetTokens(GapAwareTrackingToken.newInstance(index - 1, Collections.emptySortedSet()));
     } catch (UnableToClaimTokenException e) {
        // Ignore this exception and let the caller know setting the replay failed.
        this.logger.warn("Unable to claim token for trackingEventProcessor {} on id {}", trackingEventProcessorName, index - 1, e);
        return false;
     } finally {
        this.logger.info("Starting replay for trackingEventProcessor {} on id {}", trackingEventProcessorName, index - 1);
        trackingEventProcessor.start();
     }
     return true;
  }
}

該服務將使用Axon配置來獲取跟蹤事件處理器並開始重播。

心得

使用重放管理一年以上後,我們希望將一些發現與大小分享。

事件重播中的障礙在跟蹤事件處理中,事件是在不同的執行緒中處理的,這使得錯誤處理更加複雜。我們的錯誤處理不正確,每當發生錯誤時,重播令牌就會卡住。令牌儲存中的令牌失去了所有者,我們不得不重新部署服務以讓Axon分配新的所有者。 

重播API的粘性會話我們遇到了一種情況,我們在負載均衡器上使用了粘性會話,而跟蹤事件處理器未在我們路由到的例項上執行,因此未啟動重放過程。當您的例項達到負載平衡時,最好實現輪詢並重復呼叫。 

重播批量。在談論重放時,處理速度(時間)是非常重要的因素。根據複雜性,要​​重播的事件數和投影的記憶體利用率,重播可能要花費幾分鐘到幾天的時間。為了優化您的重放功能,可以設定跟蹤事件處理器的批處理大小。要了解這如何使您的重放體驗受益,應該知道Axon處理事件時會發生什麼。對於每個批次,Axon都需要更新跟蹤令牌,如果您使用的是事務性儲存,它將開始並提交資料庫事務。如果您一次處理一個事件,這會帶來很多開銷,並且可能會受益於更大的批處理量。

舉個例子 

事件重播執行狀況指標發生令牌處理器無法趕上新事件的情況。可能有多種原因,因為順序處理非常重要。您需要調整處理一個事件所需的時間。我們曾多次花一整天的時間來優化RDMS查詢,以使其達到最佳播放速度。理想情況下,一旦令牌在總修訂版上進行,對於更簡單的類似於CRUD的事件,處理單個後續事件的時間不應超過100毫秒。但是,這僅僅是一個粗略的估計。一個事件表可以輕鬆地包含數百萬個事件,如果想充分利用重播功能,則重播事件流將需要數小時而不是數天。可以記錄有關令牌位置和事件範圍之間差異的指標。

(banq注:其實利用Java 8 stream等流式概念可以方便重播事件資料)

相關文章