SpringBoot配置多数据源(动态切换,主从复制,读写分离)

SpringBoot2.x整合多数据源,基于注解动态切换数据源,主从复制,读写分离,多数据源的事务处理

1.多数据源实现主从复制,读写分离?

这里介绍的是MYSQL的主从复制实现及其原理,数据源分为主从,主数据源用于写操作,从数据源用于读操作,实现了读写分离
MYSQL主从复制实现以及原理

2.在配置文件里面配置主从数据源相关信息以及连接池相关信息

##############配置端口
server.port=8089

###########################################################
#**********************多数据源配置*************************
############################################################
######################主数据库配置(写入)##############
spring.datasource.master.url=jdbc:mysql://192.168.223.129:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
######################从库配置(读取)##############
spring.datasource.slave.url=jdbc:mysql://192.168.223.130:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver

####################################druid连接池配置-开始##########################################
#连接池类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
### 配置初始化大小、最小、最大
#数据库连接池初始化大小
spring.datasource.initialSize=100
#最小空闲数
spring.datasource.minIdle=500
#最大活跃数
spring.datasource.maxActive=1000
#配置获取连接等待超时的时间
spring.datasource.maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#测试连接
spring.datasource.validationQuery=select 'x'
#申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
spring.datasource.testWhileIdle=true
#获取连接时执行检测,建议关闭,影响性能
spring.datasource.testOnBorrow=false
#归还连接时执行检测,建议关闭,影响性能
spring.datasource.testOnReturn=false
#是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
spring.datasource.poolPreparedStatements=false
#开启poolPreparedStatements后生效
spring.datasource.maxOpenPreparedStatements=20
#配置扩展插件,常用的插件有=>stat:监控统计  log4j2:日志  wall:防御sql注入
pring.datasource.filters.commons-log.connection-logger-name=stat,wall,log4j2
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
#### Druid WebStatFilter配置,url统计
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions='*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*'
#### Druid StatViewServlet配置,监控界面配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=root
spring.datasource.druid.stat-view-servlet.login-password=123456
########################druid连接池配置-结束##########################################

###########################################################
#**********************配置日志文件名***********************
############################################################
logging.config=classpath:log4j2-prod.xml
###配置日志输出
spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false

###########################################################
#**********************不显示排除日志依赖信息****************
############################################################
logging.level.org.springframework.boot.autoconfigure=ERROR

###########################################################
#**********************thymeleaf配置***********************
############################################################
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

###########################################################
#**********************redis配置***********************
############################################################
redis.host=120.78.192.109
redis.port=6379
redis.timeout=3
redis.password=123456
redis.poolMaxTotal=100
redis.poolMaxIdle=50
redis.poolMaxWait=20

3.Springboot实现数据源

1.自定注解类的实现

默认是主数据源

/**
 * 自定义数据源注解,默认主库(master)
 *
 * @author Administrator
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "master";
}
2.配置数据源,以及多数据源事务

/**
 * @author Administrator
 * 配置数据库信息
 */
@Configuration
public class DataSourceConfig {
    /**
     * 主库
     */
    public static final String DB_SECKILL_GOOD = "master";
    /**
     * 用从库
     */
    public static final String DB_SECKILL_USER = "slave";

    /**
     * name中master作为主数据源
     * (@Primary)该注解声明是默认数据源
     *
     * @return 商品数据源
     */
    @Bean(name = "master")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource goodDateSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * name中slave作为从数据源
     * (@Primary)该注解声明是默认数据源
     *
     * @return 用户数据源
     */
    @Bean(name = "slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource userDateSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 默认数据源配置和多数据源配置
     *
     * @return 数据源
     */
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(goodDateSource());
        // 配置多数据源
        Map<Object, Object> dataBaseMap = new HashMap<>(16);
        dataBaseMap.put(DB_SECKILL_USER, userDateSource());
        dataBaseMap.put(DB_SECKILL_GOOD, goodDateSource());
        dynamicDataSource.setTargetDataSources(dataBaseMap);
        return dynamicDataSource;
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
// 指定扫描的xml文件所在位置,在配置文件里面配置,会报Invalid bound statement
        Resource[] resources = new PathMatchingResourcePatternResolver()
                .getResources("classpath:mybatis/*.xml");
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setMapperLocations(resources);
        bean.setConfiguration(configuration);
        return bean.getObject();
    }

    /**
     * 事务管理
     *
     * @param dataSource 数据源
     * @return 事务管理
     */
    @Bean(name = "sqlTransactionManager")
    public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
3.获得和设置上下文环境 改变上下文数据源的名称
**
 * 获得和设置上下文环境 主要负责改变上下文数据源的名称
 * @author Administrator
 */
@Component
@Slf4j
public class DataSourceContextHolder {
    /**
     * 线程独立
     */
    private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    /**
     * 主库
     */
    public static final String DB_SECKILL_GOOD = "master";


    /**
     * 获取数据源名
     *
     * @return 数据库名
     */
    public static String getDataBaseType() {
        return contextHolder.get();
    }

    /**
     * 设置数据源名(切换数据源)
     *
     * @param dataBase 数据库类型
     */
    public static void setDataBaseType(String dataBase) {
        log.info("设置数据源:" + dataBase);
        contextHolder.set(dataBase);
    }

    /**
     * 清除数据源名
     */
    public static void clearDataBaseType() {
        contextHolder.remove();
    }

}

4.建立动态数据源
/**
 * @author Administrator
 * 建立动态数据源
 */
@Configuration
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 获取当前数据源并打印日志记录
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        log.error("当前数据源:" + DataSourceContextHolder.getDataBaseType());
        return DataSourceContextHolder.getDataBaseType();
    }
}
5.使用AOP动态切换数据源
/**
 * @author Administrator
 * 动态切换数据源类
 */
@Aspect
@Component
@Order(-1)
@Slf4j
public class DynamicDataSourceAspect {

    @Before("@annotation(DataSource)")
    public void beforeSwitchDataSource(JoinPoint point) {
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DB_SECKILL_GOOD;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            // 判断是否存在@DataSource注解
            if (method.isAnnotationPresent(DataSource.class)) {
                DataSource annotation = method.getAnnotation(DataSource.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            log.error("动态切换数据源失败");
        }
        // 切换数据源
        DataSourceContextHolder.setDataBaseType(dataSource);
    }

    @After("@annotation(DataSource)")
    public void afterSwitchDataSource(JoinPoint point) {
        DataSourceContextHolder.clearDataBaseType();
    }
}

4.测试

1.我们前面在配置文件配置了druid的监控界面,登录进去查看数据源

监控界面相关信息

SpringBoot配置多数据源(动态切换,主从复制,读写分离)_第1张图片

登录监控界面(localhost:8089/druid/index.html)

SpringBoot配置多数据源(动态切换,主从复制,读写分离)_第2张图片查看数据源,可以看到里面有主从两个数据源
SpringBoot配置多数据源(动态切换,主从复制,读写分离)_第3张图片
查看数据源配置,发现连接池配置没有生效,那么只需要按主从那样配置即可生效
SpringBoot配置多数据源(动态切换,主从复制,读写分离)_第4张图片
代码测试如下
SpringBoot配置多数据源(动态切换,主从复制,读写分离)_第5张图片
5,完整的实现请参考
springboot整合多数据源

你可能感兴趣的:(Springboot,练习项目,原理,多数据源,读写分离,动态切换数据源,主从复制)