EventStroe
1.EventBus与EventStore的关系
2.什么是EventStore
EventSourcingRepository,需要通过EventStore来存储聚合产生的事件,并从其中加载事件。EventStore继承了EventBus接口,也就是说具备EventBus的路由功能,除此之外还具备:
- 持久化已发布的事件
- 支持根据指定的聚合ID,查询该聚合的历史事件
3.所有EventStore总览
4.Axon启动器
依赖
<!-- 在 POM 文件中添加依赖 -->
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>${axon.version}</version>
</dependency>
EventStroe自动配置规则
- 只需在依赖中声明axon-spring-boot-starter,Axon就会自动配置事件存储
- 若从axon-spring-boot-starter中排除axon-server-connector依赖,Axon会根据以下规则自动配置EventStore
5.EmbeddedEventStore
介绍
作为非Axon Server选项,Axon提供了EmbeddedEventStore。它本身不直接处理事件的存储和检索,而是将这一职责委托给EventStorageEngine
Axon提供了多种EventStorageEngine实现,适用于不同的存储场景
JpaEventStorageEngine
介绍
JpaEventStorageEngine将事件存储在兼容JPA的数据源中,事件以entry形式存储,每个entry包含事件的序列化数据,以及用于快速查询的元数据字段
要使用JpaEventStorageEngine,类路径中必须包含JPA注解(jakarta.persistence);同时也兼容旧版的javax.persistence注解(需通过legacyjpa命名空间使用旧版JpaEventStorageEngine)
核心配置
默认情况下,事件存储需要在持久化上下文(通常定义在META-INF/persistence.xml文件中)中注册DomainEventEntry和SnapshotEventEntry两个类(均位于org.axonframework.eventsourcing.eventstore.jpa包下),示例配置如下:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="eventStore" transaction-type="RESOURCE_LOCAL">
<class>org.axonframework.eventsourcing.eventstore.jpa.DomainEventEntry</class>
<class>org.axonframework.eventsourcing.eventstore.jpa.SnapshotEventEntry</class>
</persistence-unit>
</persistence>
- 示例中为EventStroe单独配置了持久化单元,你也可以将上述两个类添加到其他已有的持久化单元配置中
- 这一步的核心是将JpaEventStorageEngine依赖的DomainEventEntry类注册到持久化上下文,确保JPA能识别并映射该实体
唯一约束
Axon通过锁机制防止两个线程同时访问同一个聚合,但在多JVM共享同一数据库的场景下,该机制无法生效,此时需依赖数据库自身的冲突检测能力
当多个进程并发访问EventStore时,会触发键约束冲突,因为事件表要求同一聚合+同一序列号只能对应一个事件,若为已存在的聚合+序列号插入第二个事件,会直接报错
JpaEventStorageEngine能检测此类错误并将其转换为ConcurrencyException,但不同数据库的错误码格式不同:
- 若向JpaEventStorageEngine注册了DataSource,它会自动检测数据库类型,并识别对应的键约束冲突错误码
- 也可手动提供PersistenceExceptionResolver实例,自定义判断异常是否为键约束冲突
- 若未提供DataSource或PersistenceExceptionTranslator,数据库驱动抛出的异常会直接向上传递,不做转换
EntityManagerProvider
默认情况下,JpaEventStorageEngine需要EntityManagerProvider实现来提供其所需的EntityManager实例(支持应用托管的持久化上下文)。EntityManagerProvider的核心职责是确保返回正确的EntityManager实例,Axon提供了多种实现以适配不同场景:
- SimpleEntityManagerProvider:在构造时接收EntityManager实例并直接返回,适用于容器托管的上下文
- ContainerManagedEntityManagerProvider:返回默认的持久化上下文,是JPA操作EventStore的默认实现
从javax.persistence迁移到jakarta.persistence
自Axon4.6.0版本起,默认使用jakarta命名空间。为适配这一变更,JpaTokenStore等类提供了新旧两个版本(分别对应javax和jakarta)
若需为JpaEventStorageEngine指定名为myPersistenceUnit的持久化单元,可自定义EntityManagerProvider实现:
public class MyEntityManagerProvider implements EntityManagerProvider {
private EntityManager entityManager;
@Override
public EntityManager getEntityManager() {
return entityManager;
}
@PersistenceContext(unitName = "myPersistenceUnit")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}
扩展与优化
DomainEventEntry和SnapshotEventEntry能满足大多数场景,但如果需要自定义元数据或为不同聚合类型分配不同表,可通过继承JpaEventStorageEngine并覆盖其来调整行为
此外,Hibernate等持久化provider会在EntityManager中使用一级缓存,查询中使用或返回的实体都会附加到EntityManager,仅在事务提交或显式调用clear()时才会清除(尤其在事务上下文内执行查询时)。若加载大量事件流,可能导致OutOfMemoryException,解决方案如下:
- 仅查询非实体对象(如使用JPA的SELECT new SomeClass(parameters) FROM … 语法)
- 拉取一批事件后,调用EntityManager.flush()和EntityManager.clear()清理缓存
配置
原生API:
public class AxonConfig {
// 省略其他配置方法...
public Configurer jpaEventStorageConfigurer(
EntityManagerProvider entityManagerProvider,
TransactionManager transactionManager
) {
return DefaultConfigurer.jpaConfiguration(entityManagerProvider, transactionManager);
}
}
SpringBoot:
@Configuration
public class AxonConfig {
// 省略其他配置方法...
// EmbeddedEventStore 将事件的存储/检索委托给 EventStorageEngine
@Bean
public EventStore eventStore(
EventStorageEngine storageEngine,
GlobalMetricRegistry metricRegistry
) {
return EmbeddedEventStore.builder()
.storageEngine(storageEngine)
.messageMonitor(metricRegistry.registerEventBus("eventStore"))
.spanFactory(spanFactory)
// ...(其他配置)
.build();
}
// JpaEventStorageEngine 将事件存储在兼容 JPA 的数据源中
@Bean
public EventStorageEngine eventStorageEngine(
Serializer serializer,
PersistenceExceptionResolver persistenceExceptionResolver,
@Qualifier("eventSerializer") Serializer eventSerializer,
EntityManagerProvider entityManagerProvider,
TransactionManager transactionManager
) {
return JpaEventStorageEngine
.builder()
.snapshotSerializer(serializer)
.persistenceExceptionResolver(persistenceExceptionResolver)
.eventSerializer(eventSerializer)
.entityManagerProvider(entityManagerProvider)
.transactionManager(transactionManager)
// ...(其他配置)
.build();
}
}
JdbcEventStorageEngine
介绍
JdbcEventStorageEngine使用JDBC Connection将事件存储在兼容JDBC的数据源中(通常是关系型数据库),理论上所有提供JDBC驱动的存储都可作为其底层实现
与JpaEventStorageEngine类似,JdbcEventStorageEngine也以entry形式存储事件(默认每个事件对应表中的一行),并使用两个独立的表分别存储事件和快照
核心依赖
JdbcEventStorageEngine通过ConnectionProvider获取数据库连接,通常直接从DataSource中获取。Axon会将连接与工作单元绑定,确保同一工作单元内使用同一个连接,即使同一线程中嵌套多个工作单元,也能保证所有EventStore操作在同一个事务中完成
配置
原生API:
public class AxonConfig {
// 省略其他配置方法...
public void configureJdbcEventStorage(
Configurer configurer,
ConnectionProvider connectionProvider,
EventTableFactory eventTableFactory
) {
configurer.configureEmbeddedEventStore(config -> {
JdbcEventStorageEngine storageEngine = JdbcEventStorageEngine
.builder()
.snapshotSerializer(config.serializer())
.connectionProvider(connectionProvider)
.transactionManager(config.getComponent(TransactionManager.class))
.eventSerializer(config.eventSerializer())
// ...(其他配置)
.build();
// 若尚未创建数据库表结构,可调用 createSchema 方法
storageEngine.createSchema(eventTableFactory);
return storageEngine;
});
}
}
SpringBoot:
若类路径中存在JDBC,Axon的JdbcAutoConfiguration会自动生成JdbcEventStorageEngine。如需创建表结构,可参考以下配置:
@Configuration
public class AxonConfig {
// 省略其他配置方法...
// EmbeddedEventStore 将事件的存储/检索委托给 EventStorageEngine
@Bean
public EventStore eventStore(
EventStorageEngine storageEngine,
GlobalMetricRegistry metricRegistry
) {
return EmbeddedEventStore
.builder()
.storageEngine(storageEngine)
.messageMonitor(metricRegistry.registerEventBus("eventStore"))
.spanFactory(spanFactory)
// ...(其他配置)
.build();
}
// JdbcEventStorageEngine 将事件存储在兼容 JDBC 的数据源中
@Bean
public EventStorageEngine storageEngine(
Serializer serializer,
ConnectionProvider connectionProvider,
@Qualifier("eventSerializer") Serializer eventSerializer,
TransactionManager transactionManager,
EventTableFactory tableFactory
) {
JdbcEventStorageEngine storageEngine = JdbcEventStorageEngine
.builder()
.snapshotSerializer(serializer)
.connectionProvider(connectionProvider)
.eventSerializer(eventSerializer)
.transactionManager(transactionManager)
// ...(其他配置)
.build();
// 若尚未创建数据库表结构,可调用 createSchema 方法
storageEngine.createSchema(tableFactory);
return storageEngine;
}
}
Spring环境下的数据源推荐
对于Spring用户,建议使用SpringDataSourceConnectionProvider,它能将DataSource中的连接绑定到已有的事务中,确保事务一致性
SQL语句自定义
不同数据库的最优SQL语句存在差异,Axon无法覆盖所有场景,因此允许自定义JdbcEventStorageEngine使用的SQL语句:
- 可通过JdbcEventStorageEngineStatements工具类查看默认语句
- 所有可调整的语句均位于org.axonframework.eventsourcing.eventstore.jdbc.statements包下
- 可通过JdbcEventStorageEngine.Builder自定义这些语句的构建逻辑
MongoEventStorageEngine
介绍
MongoDB是基于文档的NoSQL数据库,其可扩展性使其适合作为EventStore。Axon提供MongoEventStorageEngine(位于axon-mongo模块),将MongoDB作为底层存储
有两个独立的集合:一个用于事件流,一个用于快照
存储策略
默认情况下,MongoEventStorageEngine为每个事件创建一个独立文档;也可通过调整StorageStrategy改变存储方式,Axon提供的DocumentPerCommitStorageStrategy会将同一提交中的所有事件(即同一DomainEventStream中的事件)存储为单个文档
两种策略的对比:
- 单文档存储:提交操作原子性强,无论事件数量多少都只需一次数据库往返;但缺点是难以直接查询单个事件(如领域模型重构时,难以将事件从一个聚合迁移到另一个)
- 多文档存储(默认):查询灵活,但需多次数据库往返
配置
原生API:
public class AxonConfig {
// 省略其他配置方法...
public void configureMongoEventStorage(Configurer configurer, MongoTemplate mongoTemplate) {
configurer.configureEmbeddedEventStore(config -> MongoEventStorageEngine
.builder()
.mongoTemplate(mongoTemplate)
// ...(其他配置)
.build()
);
}
}
SpringBoot:
@Configuration
public class AxonConfig {
// 省略其他配置方法...
// EmbeddedEventStore 将事件的存储/检索委托给 EventStorageEngine
@Bean
public EventStore eventStore(EventStorageEngine storageEngine,
GlobalMetricRegistry metricRegistry) {
return EmbeddedEventStore
.builder()
.storageEngine(storageEngine)
.messageMonitor(metricRegistry.registerEventBus("eventStore"))
.spanFactory(spanFactory)
// ...(其他配置)
.build();
}
// MongoEventStorageEngine 将每个事件存储为 MongoDB 中的独立文档
@Bean
public EventStorageEngine storageEngine(MongoClient client) {
return MongoEventStorageEngine
.builder()
.mongoTemplate(DefaultMongoTemplate.builder()
.mongoDatabase(client)
.build())
// ...(其他配置)
.build();
}
}
6.EventStore工具类
- InMemoryEventStorageEngine:基于内存的EventStore,往往用于短期工具或测试场景
- SequenceEventStorageEngine:是一个包装类,可同时关联两个EventStorageEngine,读取用两个,追加用第二个
- FilteringEventStorageEngine:允许根据谓词过滤事件,仅符合条件的事件会被存储
7.事件序列化
介绍
EventStore需要通过序列化将事件转换为可存储的格式。Axon默认使用XStreamSerializer(基于XStream框架),将事件序列化为XML格式
推荐
Axon还提供JacksonSerializer(基于Jackson框架),将事件序列化为JSON格式,序列化结果更紧凑,但要求事件类符合Jackson的序列化约定(或通过配置适配)
自定义
只需实现Serializer接口,并将EventStore配置为使用该实现
配置
原生API
// 返回已配置默认组件的 Configurer 实例
// 显式将 JacksonSerializer 设为事件序列化器
Configurer configurer = DefaultConfigurer.defaultConfiguration()
.configureEventSerializer(c -> JacksonSerializer.builder().build());
SpringBoot Properties
# 在 application.properties 中配置
axon.serializer.events=jackson
# 可选值:java(原生序列化)、xstream、jackson
SpringBoot自定义Bean
// 在 @Configuration 类中定义
@Qualifier("eventSerializer")
@Bean
public Serializer eventSerializer() {
return JacksonSerializer.builder().build();
}
与其他对象序列化的区别
Axon允许为EventStore和其他对象(如命令、快照、Saga等) 配置不同的序列化器:
- XStreamSerializer能序列化几乎所有对象,适合作为默认序列化器,但输出格式不便于跨应用共享
- JacksonSerializer输出的JSON格式更通用,且事件类通常符合其序列化约定,因此更适合作为事件序列化器
若未显式配置eventSerializer,事件会使用全局默认序列化器(默认是XStreamSerializer)进行序列化