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

问题描述 投票:0回答:1

我使用mysql作为事件存储,所以axon-server-connector被排除在classpath之外。我的用例描述如下。

  1. Spring Boot 2.1.7.RELEASE和axon 4.3.2。
  2. 我计划有三个数据库,分别用于axon事件存储、投影写入和投影读取。
@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.eventsourcing.eventstore.jpa是否足够?也许我还需要添加token包org.axonframework.eventhandling.tokenstore.jpa和soga包?我将使用soga store。(2) 这里的包和之前的包一样吗?persistenceUnit的名字应该是什么?

例子项目已经上传到github了。https:/github.comsincosmosaxon-multiple-databases。 我无法让应用程序运行.我做的一切都对吗?我参考了以下的例子 https:/github.comAxonIQgiftcard-demo。但多数据库版本是基于axon 2.0的,也需要配置axon命令总线。

目标似乎很简单,就是在一个axon框架应用中配置多个数据库(一个用于事件存储),但即使我花了几天时间,还是一无所获。

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

**************************** 尊敬的各位领导、各位来宾、各位朋友,大家好!我是来自北京的王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生,我是王先生。

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

特别是对于axon事件存储,db配置显示为belown,我启动应用程序后(如果是第一次,会自动创建事件存储相关表),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的堆栈,没有发现死锁。我设置了datasource leakDetectionThreshold为10000,但正如你所看到的,没有打印任何datasource泄漏信息。你能帮忙解决这个问题吗?

**************************** 我想问一下,我的问题是,我的问题是什么呢?

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

spring-data-jpa event-sourcing axon
1个回答
2
投票

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

Axon并不直接使用EntityManager,而是所有组件都需要一个EntityManager。相反,所有的组件都需要一个EntityManagerProvider。你也许可以利用这个优势。

如果你想让所有的Axon组件都使用某个数据库,只需定义一个EntityManagerProvider Bean,它返回连接到该数据库的EntityManager。Spring完全管理着EntityManager,所以你只需要一个实例来管理所有的EntityManager Session。

如果你想让不同的组件使用不同的EntityManagers(例如,一个数据库中的Event Store,另一个数据库中的Tokens和Sagas),那么你需要自己配置这些组件。有时,从AutoConfiguration类中复制bean定义并调整它们以满足你的需求是最简单的。请看 https:/github.comAxonFrameworkAxonFrameworktreemasterspring-boot-autoconfiguresrcmainjavaorgaxonframeworksspringbootautoconfig。

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

  • org.axonframework.eventhandling.tokenstore(针对令牌)。
  • org.axonframework.modeling.saga.repository.jpa(用于sagas)
  • org.axonframework.eventourcing.eventstore.jpa (用于事件商店)

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

© www.soinside.com 2019 - 2024. All rights reserved.