数据库动态读写分离 使用readOnly=true使用从库

/**

  • 读写库与只读库自动切换的Datasource定义。 当定义为@Transactional(readOnly = true) 时,自动切换到只读库。

  • Aws本身支持多个只读库的负载均衡,此处不做多个只读库的设计。

  • @date 2018/9/19
    */
    @Slf4j
    public class PendaRwDataSource extends AbstractRoutingDataSource {

    private final String DATASOURCE_DEFINE_ERROR_CODE = “ORM_DS_DEF_001”;
    private final String DATASOURCE_DEFINE_ERROR_MSG = “Datasource key must be master or slave”;

    public static final String MASTER = “master”;
    public static final String SLAVE = “slave”;

    private boolean readReplicaEnabled = false;

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
    //当key不是master或者slave的时候,抛出异常;
    for (Object key : targetDataSources.keySet()) {
    String keyStr = (String) key;
    if (!keyStr.equals(MASTER) && !keyStr.equals(SLAVE)) {
    log.error(DATASOURCE_DEFINE_ERROR_CODE + " - " + DATASOURCE_DEFINE_ERROR_MSG);
    throw new InternalServiceException(DATASOURCE_DEFINE_ERROR_CODE, DATASOURCE_DEFINE_ERROR_MSG);
    }
    }
    // 当key 包含Slave时,标记可读到只读备份库。
    if (targetDataSources.containsKey(SLAVE)) {
    readReplicaEnabled = true;
    }
    super.setTargetDataSources(targetDataSources);
    }

    @Override
    protected Object determineCurrentLookupKey() {
    if (TransactionSynchronizationManager.isCurrentTransactionReadOnly() && readReplicaEnabled) {
    log.debug("-----------------------determineCurrentLookupKey is " + SLAVE);
    return SLAVE;
    }
    log.debug("----------------------determineCurrentLookupKey is " + MASTER);
    return MASTER;
    }
    }

使用 determineCurrentLookupKey 来判断使用的是主库还是从库

package com.pendanaan.configuration;

import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.pendanaan.core.spring.SpringContextHolder;
import com.pendanaan.datasource.PendaRwDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.HashMap;
import java.util.Map;

/**

  • @date 2018/9/3
    */
    @Slf4j
    @Configuration
    public class RwDataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SpringContextHolder initSpringContextHolder() {
    return new SpringContextHolder();
    }

    @Configuration
    @ConditionalOnProperty(name = “spring.datasource.druid.slave.slave-enable”, havingValue = “true”)
    public static class SlaveDataSourceAutoConfiguration {

     @Bean(name = "slave")
     @ConfigurationProperties("spring.datasource.druid.slave")
     public DruidDataSource dataSourceSlave() {
         log.info("initializing slave data source.");
         return new DruidDataSource();
     }
    

    }

    /**

    • 初始化自定义的StatFilter配置

    • @return StatFilter
      */
      @Bean
      public StatFilter initStatFilter() {
      StatFilter statFilter = new StatFilter();
      statFilter.setDbType(“mysql”);
      statFilter.setLogSlowSql(true);
      statFilter.setSlowSqlMillis(2000);
      statFilter.setMergeSql(true);

      return statFilter;
      }

    /**

    • 初始化自定义的WallFilter配置

    • @return WallFilter
      */
      @Bean
      public WallFilter initWallFilter() {
      WallConfig config = new WallConfig();
      config.setDropTableAllow(false);
      config.setCreateTableAllow(false);
      config.setAlterTableAllow(false);
      config.setTruncateAllow(false);
      config.setDeleteAllow(false);

      WallFilter wallFilter = new WallFilter();
      wallFilter.setDbType(“mysql”);
      wallFilter.setConfig(config);

      return wallFilter;
      }

    @Bean
    public Slf4jLogFilter initSlf4jLogFilter() {
    return new Slf4jLogFilter();
    }

    @Primary
    @Bean(name = “master”)
    @ConfigurationProperties(“spring.datasource.druid.master”)
    public DruidDataSource dataSourceMaster() {
    return new DruidDataSource();
    }

    @Bean(name = “rwDataSource”)
    @DependsOn(“initSpringContextHolder”)
    public PendaRwDataSource initPendaRwDataSource(@Qualifier(“master”) DruidDataSource master) {
    PendaRwDataSource datasourceProxy = new PendaRwDataSource();
    Map<Object, Object> targetDataSources = new HashMap<>();
    targetDataSources.put(PendaRwDataSource.MASTER, master);

     DruidDataSource slave = SpringContextHolder.getBean("slave", DruidDataSource.class);
     if (null != slave) {
         targetDataSources.put(PendaRwDataSource.SLAVE, slave);
     }
    
     datasourceProxy.setTargetDataSources(targetDataSources);
     datasourceProxy.setDefaultTargetDataSource(master);
    
     return datasourceProxy;
    

    }

    @Primary
    @Bean
    public DataSourceTransactionManager transactionManager(PendaRwDataSource DataSource) {
    return new DataSourceTransactionManager(DataSource) {
    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
    // 必须在开始事务前,将readOnly状态变更正确,
    // 否则在PendaRwDataSource的determineCurrentLookupKey无法获取到争取的readOnly值
    TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
    super.doBegin(transaction, definition);
    }
    };
    }

}

在这里插入图片描述
可知,通过spring的GenericBeanDefinition 和 读取properties的方法,以及多租户实现类,可以通过appname去设置数据源,
GenericBeanDefinition define = new GenericBeanDefinition();
define.setBeanClass(PendaRwDataSource.class);
关键点, 设置了默认数据源和 动态数据源, 之后就可以 通过 determineCurrentLookupKey , 来实现动态数据源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值