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

2023-12-26

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

  1. Spring Boot 2.1.7.RELEASE 和 axon 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 data 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.eventcommerce.eventstore.jpa就足够了吗?也许我还需要添加令牌包 org.axonframework.eventhandling.tokenstore.jpa 和 soga 包?我会使用soga商店。
(2) 这里的套餐和之前的一样吗? persistenceUnit 的名称应该是什么?

示例项目上传到github:https://github.com/sincosmos/axon-multiple-databases https://github.com/sincosmos/axon-multiple-databases
我无法运行该应用程序。 我做的一切都正确吗?我参考了这个例子https://github.com/AxonIQ/giftcard-demo https://github.com/AxonIQ/giftcard-demo,但多数据库版本是基于axon 2.0,还需要配置axon命令总线。

目标看起来很简单,在 axon 框架应用程序中配置多个数据库(一个用于事件存储),但即使我花了几天时间,我仍然一无所获。

有人可以给我一些建议或帮助吗?我将非常感激。

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

阅读 Allard 的答案后我取得了进展,现在我可以为应用程序配置多个数据库。源码已上传至githubhttps://github.com/sincosmos/axon-multiple-databases.git https://github.com/sincosmos/axon-multiple-databases.git

特别是,对于 axon 事件存储,数据库配置如下所示。

@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 工作台中检查连接状态时,这些连接的状态为“睡眠”。更改连接池大小没有帮助。我还检查了jvm的堆栈,没有发现死锁。我将数据源泄漏检测阈值设置为 10000,但正如您所看到的,没有打印数据源泄漏信息。 你能帮忙吗?

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

事实证明,当事件处理器尝试访问 mysql 事件存储时,发生了“javax.persistence.TransactionRequiredException:没有事务正在进行”。我为每个数据源配置了事务管理器,但错误仍然存​​在。不知道发生了什么......


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

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

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

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

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

  • org.axonframework.eventhandling.tokenstore(用于令牌)
  • org.axonframework.modelling.saga.repository.jpa(用于 sagas)
  • org.axonframework.eventoperative.eventstore.jpa(用于事件存储)

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Axon框架:如何配置多个数据库? 的相关文章

随机推荐