springboot hikari 多数据源 负载均衡 mysql

项目是gradle项目,build.gradle对应maven的pom.xml

build.gradle

dependencies {//类似于maven的dependencies
    implementation 'org.springframework.boot:spring-boot-starter-web'//spring web
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'//jdbc
    runtimeClasspath 'mysql:mysql-connector-java'//mysql驱动
    implementation 'org.springframework.boot:spring-boot-starter-aop'//切面
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'//redis
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'//mybatis
}

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    #Hikari连接池
    type: com.zaxxer.hikari.HikariDataSource
    mysql1:
      jdbc-url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
      username: x
      password: x
    mysql2:
      jdbc-url: jdbc:mysql://192.168.18.253:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
      username: x
      password: x

SpringbootApplication.java

排除数据源自动配置,改成手动配置

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})//排除数据源自动配置,改成手动配置
public class GradledemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(GradledemoApplication.class, args);
    }
}

HikariConfig.java

import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

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

@Configuration
public class HikariConfig {
    /**
     * 配置别名
     */
    @Value("${mybatis.type-aliases-package}")
    private String typeAliasesPackage;

    /**
     * 配置mapper的扫描,找到所有的mapper.xml映射文件
     */
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.mysql1")
    public HikariDataSource mysql1DataSource() {
        return new HikariDataSource();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.mysql2")
    public HikariDataSource mysql2DataSource() {
        return new HikariDataSource();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put("mysql1", mysql1DataSource());
        targetDataSources.put("mysql2", mysql2DataSource());
        return new DynamicDataSource(mysql1DataSource(), targetDataSources);
    }

    /**
     * SqlSessionFactory 配置并放入容器中
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return sqlSessionFactoryBean.getObject();
    }
}

DynamicDataSource.java

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        this.setDefaultTargetDataSource(defaultTargetDataSource);
        this.setTargetDataSources(targetDataSources);
        this.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceAspect.DATA_SOURCE_LIST.get((int) redisTemplate.opsForValue().get(DataSourceAspect.INDEX));
    }
}

DataSourceAspect.java

由切面去控制执行数据库操作时动态切换数据源
mysql1、mysql2对应application.yml的配置

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Order(1)
@Component
public class DataSourceAspect {
    public static final String INDEX = "index";
    public static final List<String> DATA_SOURCE_LIST = new ArrayList<String>(2) {{
        this.add("mysql1");
        this.add("mysql2");
    }};

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Around("execution(public * com.fu.gradledemo.mapper.*.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        try {
            robin();//轮询数据库
            return point.proceed();
        } catch (MyBatisSystemException e) {
            robin();//连接不上就再轮询一次,获取另外一个mysql数据库连接
            return point.proceed();
        }
    }

    /**
     * 轮询mysql数据库
     */
    public void robin() {
        //没有key就创建key
        if (!redisTemplate.hasKey(INDEX)) {
            redisTemplate.opsForValue().set(INDEX, 0);
        } else {
            //有key就直接获取
            int getIndex = (int) redisTemplate.opsForValue().get(INDEX);
            //超过list集合的长度减一就重新赋值(轮询),利用redis单线程的特性存放全局index下标
            if (getIndex >= DATA_SOURCE_LIST.size() - 1) {
                redisTemplate.opsForValue().set(INDEX, 0);
            } else {
                redisTemplate.opsForValue().set(INDEX, ++getIndex);
            }
        }
    }
}

模拟是否成功动态切换数据源

localhost和253都创建demo数据库,user表里面的id一样,数据不一样,来确认数据是否真的动态切换。

localhost的demo库user表

在这里插入图片描述

253的demo库user表

name全部给它加个2
在这里插入图片描述
启动springboot项目。

第一次访问接口

在这里插入图片描述

第二次访问接口

在这里插入图片描述

题外

想要实现某些接口使用mysql1,某些接口使用mysql2。则使用aop切面+自定义注解的形式,只要加了自定义注解,就访问另外一个库,否则访问默认库。
数据不同步,则可以实现一主一从、互为主从的mysql。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值