apache / incubator-seata

:fire: Seata is an easy-to-use, high-performance, open source distributed transaction solution.
https://seata.apache.org/
Apache License 2.0
25.37k stars 8.79k forks source link

seata手动设置代理数据源,主从的时候遇到了问题 #4311

Closed Shmily-candy closed 2 years ago

Shmily-candy commented 2 years ago

Ⅰ. Issue Description

我hikari cp手动代理数据源配置了主从,注册源的时候:从库的配置覆盖了主库,导致回滚的时候 产生只读的问题,请问如何解决?

Ⅱ. Describe what happened

If there is an exception, please attach the exception trace:

Just paste your stack trace here!

Ⅲ. Describe what you expected to happen

Ⅳ. How to reproduce it (as minimally and precisely as possible)

  1. xxx
  2. xxx
  3. xxx

Ⅴ. Anything else we need to know?

Ⅵ. Environment:

Shmily-candy commented 2 years ago

package com.yb.cloud.mgr.config;

import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.yb.cloud.mgr.enums.DataSourceKey; import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.plugin.Interceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource; import java.io.IOException; import java.util.HashMap; import java.util.Map;

/**

@Configuration public class DataSourcesProxyConfig {

@Autowired
private Environment environment;

@Bean(name = "originMaster")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DataSource dataSourceMaster() {
    HikariDataSource hikariDataSource = new HikariDataSource();
    hikariDataSource.setDriverClassName(environment.getProperty("spring.datasource.dynamic.datasource.master.driver-class-name"));
    hikariDataSource.setJdbcUrl(environment.getProperty("spring.datasource.dynamic.datasource.master.url"));
    hikariDataSource.setUsername(environment.getProperty("spring.datasource.dynamic.datasource.master.username"));
    hikariDataSource.setPassword(environment.getProperty("spring.datasource.dynamic.datasource.master.password"));
    hikariDataSource.setReadOnly(Boolean.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.master.hikari.is-read-only")));
    hikariDataSource.setConnectionTimeout(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.master.hikari.connection-timeout")));
    hikariDataSource.setIdleTimeout(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.master.hikari.idle-timeout")));
    hikariDataSource.setMaxLifetime(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.master.hikari.max-lifetime")));
    hikariDataSource.setMaximumPoolSize(Integer.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.master.hikari.max-pool-size")));
    return hikariDataSource;
}

@Bean(name = "originSlave")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave")
public DataSource dataSourceSlave() {
    HikariDataSource hikariDataSource = new HikariDataSource();
    hikariDataSource.setJdbcUrl(environment.getProperty("spring.datasource.dynamic.datasource.slave.url"));
    hikariDataSource.setDriverClassName(environment.getProperty("spring.datasource.dynamic.datasource.slave.driver-class-name"));
    hikariDataSource.setUsername(environment.getProperty("spring.datasource.dynamic.datasource.slave.username"));
    hikariDataSource.setPassword(environment.getProperty("spring.datasource.dynamic.datasource.slave.password"));
    hikariDataSource.setReadOnly(Boolean.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.slave.hikari.is-read-only")));
    hikariDataSource.setConnectionTimeout(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.slave.hikari.connection-timeout")));
    hikariDataSource.setIdleTimeout(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.slave.hikari.idle-timeout")));
    hikariDataSource.setMaxLifetime(Long.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.slave.hikari.max-lifetime")));
    hikariDataSource.setMaximumPoolSize(Integer.valueOf(environment.getProperty("spring.datasource.dynamic.datasource.slave.hikari.max-pool-size")));
    return hikariDataSource;
}

@Bean(name = "master")
public DataSourceProxy masterDataSourceProxy(@Qualifier("originMaster") DataSource dataSource) {
    return new DataSourceProxy(dataSource);
}

@Bean(name = "slave")
public DataSourceProxy slaveDataSourceProxy(@Qualifier("originSlave") DataSource dataSource) {
    return new DataSourceProxy(dataSource);
}

@Bean("dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("master") DataSource dataSourceMaster,
                                    @Qualifier("slave") DataSource dataSourceSlave) {

    DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();

    Map<Object, Object> dataSourceMap = new HashMap<>(2);

    dataSourceMap.put(DataSourceKey.MASTER, dataSourceMaster);
    dataSourceMap.put(DataSourceKey.SLAVE, dataSourceSlave);

    dynamicRoutingDataSource.setDefaultTargetDataSource(dataSourceMaster);
    dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

    DynamicDataSourceContextHolder.getDataSourceKeys().addAll(dataSourceMap.keySet());

    return dynamicRoutingDataSource;
}

@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

@Bean
@ConfigurationProperties(prefix = "mybatis")
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource, PaginationInterceptor paginationInterceptor) throws IOException {
    // 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
    MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
    mybatisSqlSessionFactoryBean.setDataSource(dataSource);
    mybatisSqlSessionFactoryBean.setMapperLocations(
            new PathMatchingResourcePatternResolver().
                    getResources(environment.getProperty("mybatis-plus.mapper-locations"))
    );
    MybatisConfiguration configuration = mybatisSqlSessionFactoryBean.getConfiguration();
    if (configuration == null) {
        configuration = new MybatisConfiguration();
    }
    configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);
    //设置分页
    Interceptor[] plugins = {paginationInterceptor};
    mybatisSqlSessionFactoryBean.setPlugins(plugins);

    return mybatisSqlSessionFactoryBean;
}

}

Shmily-candy commented 2 years ago
@Override
public void registerResource(Resource resource) {
    DataSourceProxy dataSourceProxy = (DataSourceProxy) resource;
    dataSourceCache.put(dataSourceProxy.getResourceId(), dataSourceProxy);
    super.registerResource(dataSourceProxy);
}
funky-eyes commented 2 years ago

不要代理从库

funky-eyes commented 2 years ago

读写都有的场景都走主库,纯写也走主库

funky-eyes commented 2 years ago

自动代理也记得关掉

Shmily-candy commented 2 years ago

spring的数据源自动配置已经排除了,那纯读的时候 怎么整?

Shmily-candy commented 2 years ago

mp-starter 也排除了 现在是 动态路由 通过指定master还是slave来决定 读写还是读

Shmily-candy commented 2 years ago

嗯嗯 解决了 谢谢 只把 代理关掉 动态路由主从那块 从库不走代理即可。谢谢 da lao