Axon Framework:如何配置多个数据库?

Axon Framework: how to configure mutiple databases?

我正在使用 mysql 作为事件存储,因此 axon-server-connector 被排除在类路径之外。我的用例描述如下。

  1. Spring 引导 2.1。7.RELEASE 和轴突 4.3.2
  2. 我打算做三个数据库,分别用于轴突事件存储、投影写入和投影读取。
@Configuration
public class DataSourceConfiguration {
    @Primary
    @Bean("axonMaster")
    @ConfigurationProperties("spring.datasource.hikari.axon-master")
    public DataSource axon() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean("projectionRead")
    @ConfigurationProperties("spring.datasource.hikari.projection-write")
    public DataSource master() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean("projectionWrite")
    @ConfigurationProperties("spring.datasource.hikari.projection-read")
    public DataSource slave() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}
  1. 我试图用 spring 数据 jpa 配置多个数据源。初级的如下图
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "axonEntityManagerFactory",
        basePackages = "org.axonframework.eventsourcing.eventstore.jpa") // (1)
public class AxonEventStoreConfig {
    @Primary
    @Bean(name="axonEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("axonMaster") DataSource axonMaster) {
        return builder
                .dataSource(axonMaster)
                .packages("org.axonframework.eventsourcing.eventstore.jpa")
                .persistenceUnit("axonMaster") //(2)
                .build();
    }

    @Primary
    @Bean(name = "axonPlatformTransactionManager") //(3)
    public PlatformTransactionManager transactionManager(
            @Qualifier("axonEntityManagerFactory") EntityManagerFactory axonEntityManagerFactory) {
        return new JpaTransactionManager(axonEntityManagerFactory);
    }
}

关于这部分的问题是:
(1) basePackages设置成org.axonframework.eventsourcing.eventstore.jpa就够了吗?也许我还需要添加令牌包 org.axonframework.eventhandling.tokenstore.jpa 和 soga 包?我会用soga商店。
(2) 这里的套餐和之前的一样吗? persistenceUnit 的名称应该是什么?

示例项目已上传至github:https://github.com/sincosmos/axon-multiple-databases
我无法获得应用程序 运行。 我做的一切都对吗?我参考了https://github.com/AxonIQ/giftcard-demo的例子,但是多数据库版本是基于axon 2.0的,axon command bus也需要配置。

目标看似简单,在一个axon框架应用中配置多个数据库(一个用于event store),但即使我花了好几天,我仍然一事无成。

谁能给我一些建议或帮助?我将不胜感激。

/**************************** 更新 20200521 *************** **************/

看了Allard的回答我有了进步,现在我可以为应用程序配置多个数据库了。源码上传到githubhttps://github.com/sincosmos/axon-multiple-databases.git

特别是对于axon event store,数据库配置如下所示。

@Configuration
@EnableTransactionManagement
public class AxonEventStoreConfig {
    @Bean("axonMaster")
    @ConfigurationProperties("spring.datasource.hikari.axon-master")
    public DataSource axon() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name="axonEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("axonMaster") DataSource axonMaster) {
        return builder
                .dataSource(axonMaster)
                .persistenceUnit("axonMaster")
                .properties(jpaProperties())
                .packages("org.axonframework.eventhandling.tokenstore",
                        "org.axonframework.modelling.saga.repository.jpa",
                        "org.axonframework.eventsourcing.eventstore.jpa")
                .build();
    }

    /**
     * Is it right to provide EntityManagerProvider like this ???  
     * For axon event store
     * @param entityManagerFactory
     * @return
     */
    @Bean
    public EntityManagerProvider entityManagerProvider(@Qualifier("axonEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        return () -> entityManagerFactory.getObject().createEntityManager();
    }

    private Map<String, Object> jpaProperties() {
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
        props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        props.put("hibernate.hbm2ddl.auto", "update");
        props.put("hibernate.show_sql", "true");
        return props;
    }
}

在我启动应用程序后(如果是第一次,将自动创建事件存储相关表),axon 的数据库连接池将很快耗尽。贴上日志供大家参考。

Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select min(domaineven0_.global_index)-1 as col_0_0_ from domain_event_entry domaineven0_
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
19:59:57.223 [EventProcessor[com.baeldung.axon.querymodel]-0] WARN  o.a.e.TrackingEventProcessor - Fetch Segments for Processor 'com.baeldung.axon.querymodel' failed: no transaction is in progress. Preparing for retry in 1s
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select min(domaineven0_.global_index)-1 as col_0_0_ from domain_event_entry domaineven0_
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
19:59:58.293 [EventProcessor[com.baeldung.axon.querymodel]-0] WARN  o.a.e.TrackingEventProcessor - Fetch Segments for Processor 'com.baeldung.axon.querymodel' failed: no transaction is in progress. Preparing for retry in 2s
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select min(domaineven0_.global_index)-1 as col_0_0_ from domain_event_entry domaineven0_
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
20:00:00.361 [EventProcessor[com.baeldung.axon.querymodel]-0] WARN  o.a.e.TrackingEventProcessor - Fetch Segments for Processor 'com.baeldung.axon.querymodel' failed: no transaction is in progress. Preparing for retry in 4s
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select min(domaineven0_.global_index)-1 as col_0_0_ from domain_event_entry domaineven0_
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
20:00:04.465 [EventProcessor[com.baeldung.axon.querymodel]-0] WARN  o.a.e.TrackingEventProcessor - Fetch Segments for Processor 'com.baeldung.axon.querymodel' failed: no transaction is in progress. Preparing for retry in 8s
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
Hibernate: select min(domaineven0_.global_index)-1 as col_0_0_ from domain_event_entry domaineven0_
Hibernate: select tokenentry0_.segment as col_0_0_ from token_entry tokenentry0_ where tokenentry0_.processor_name=? order by tokenentry0_.segment ASC
20:00:12.531 [EventProcessor[com.baeldung.axon.querymodel]-0] WARN  o.a.e.TrackingEventProcessor - Fetch Segments for Processor 'com.baeldung.axon.querymodel' failed: no transaction is in progress. Preparing for retry in 16s
20:00:17.327 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=15, active=15, idle=0, waiting=0)
20:00:22.178 [HikariPool-2 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Pool stats (total=10, active=0, idle=10, waiting=0)
20:00:23.092 [HikariPool-3 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-3 - Pool stats (total=10, active=0, idle=10, waiting=0)

当我在 mysql workbench 中检查连接状态时,这些连接的状态是 "Sleep"。更改连接池大小没有帮助。我也检查了jvm的栈,没有发现死锁。我将数据源 leakDetectionThreshold 设置为 10000,但如您所见,没有打印任何数据源泄漏信息。 你能帮忙吗?

/**************************** 更新 20200522 *************** **************/

事实证明 "javax.persistence.TransactionRequiredException: no transaction is in progress" 发生在事件处理器试图访问 mysql 事件存储时。我为每个数据源配置了事务管理器,但错误仍然存​​在。不知道发生了什么...

使用多个数据库时,您可能无法再依赖自动配置,因为 Spring 和 Axon 不知道您要使用两个数据库中的哪一个。

Axon 不直接使用 EntityManager。相反,所有组件都需要 EntityManagerProvider。您也许可以利用它来发挥自己的优势。

如果您希望所有 Axon 组件都使用某个数据库,只需定义一个 EntityManagerProvider bean,returns 连接到该数据库的 EntityManager。 Spring 完全管理 EntityManager,因此您的所有 EntityManager 会话只需要一个实例。

如果您希望不同的组件使用不同的 EntityManagers(例如,一个数据库中的 Event Store,另一个数据库中的 Tokens 和 Sagas),那么您将需要自己配置这些组件。有时,从 AutoConfiguration 类 复制 bean 定义并调整它们以满足您的需要是最简单的。参见 https://github.com/AxonFramework/AxonFramework/tree/master/spring-boot-autoconfigure/src/main/java/org/axonframework/springboot/autoconfig

最后,您需要扫描的实体取决于您希望使用的组件。 Spring 默认情况下,引导自动配置将扫描以下 Axon 包(如果您自己未指定任何 @EntityScan):

  • org.axonframework.eventhandling.tokenstore(代币)
  • org.axonframework.modelling.saga.repository.jpa(传奇)
  • org.axonframework.eventsourcing.eventstore.jpa(事件商店)

请注意,@EnableJpaRepositories 注释用于扫描 @Repository 类。 Axon 不使用这些,因此没有必要为它们扫描 Axon 包。 Axon 确实定义了实体,所以 @EntityScan 是有意义的。