当前位置: 首页 > news >正文

spring boot实现程序运行过程中数据源动态切换

项目中有一个需求,spring boot项目连接postgres数据库的地址,是存储在etcd当中的,在程序启动后,当etcd中的地址变化时,需要程序去连接新的postgres地址。

1. 修改Datasource定义,改为使用DynamicPGDataSource,它是一个自定义类,集成了

AbstractRoutingDataSource
@Primary
    @Bean
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setInitialSize(druidInitSize);// 初始化连接数
        druidDataSource.setMinIdle(druidMinIdle); // 最小连接数
        druidDataSource.setMaxActive(druidMaxActive);// 最大连接数
        druidDataSource.setPoolPreparedStatements(true);// 开启缓存preparedStatement
        druidDataSource.setUseGlobalDataSourceStat(true);

        // 开启Druid提供的3s慢SQL监控
        Properties properties = new Properties();
        properties.put("druid.stat.mergeSql", true);
        properties.put("druid.stat.slowSqlMillis", 3000);
        druidDataSource.setConnectProperties(properties);
        try {
            druidDataSource.setFilters("stat,wall");
            druidDataSource.init();
        } catch (SQLException e) {
            log.error("构建数据库连接池异常,异常原因:{}", e);
            throw new RuntimeException(e);
        }

        DynamicPGDataSource dynamicPGDataSource = new DynamicPGDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(ConnectInfo.currentPGIP, druidDataSource);
        dynamicPGDataSource.setTargetDataSources(targetDataSources);
        dynamicPGDataSource.setDefaultTargetDataSource(druidDataSource);
        return dynamicPGDataSource;
    }

2. 类定义

public class DynamicPGDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContext.getPGDataSource();
    }
}
public class DataSourceContext {
    private static String pgDataSource;

    public static void setPGDataSource(String ds) {
        pgDataSource = ds;
    }

    public static String getPGDataSource() {
        return pgDataSource;
    }
}

3. 定义re方法,当监听到数据源IP更改之后,去切换连接到新的数据源,并且关闭老的数据源连接。

public void refreshPGDataSource(String ip) {
        if(ConnectInfo.currentPGIP.equals(ip)) {
            log.info("currentPGIP equals ip, not operate, ip:{}", ip);
        }
        DynamicPGDataSource dynamicPGDataSource = ((DynamicPGDataSource)druidDataSource);
        Field field = null;
        try {
            field = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        field.setAccessible(true);

        // 获取当前的 targetDataSources
        Map<Object, Object> currentDataSources = null;
        try {
            currentDataSources = (Map<Object, Object>) field.get(dynamicPGDataSource);
            if(!currentDataSources.containsKey(ip)) {
                DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
                String address = String.format("jdbc:postgresql://%s:5432/dsgdb?characterEncoding=utf-8&useSSL=false", ip);
                druidDataSource.setUrl(address);
                druidDataSource.setUsername(username);
                druidDataSource.setPassword(password);
                druidDataSource.setDriverClassName(driverClassName);
                druidDataSource.setInitialSize(20);// 初始化连接数
                druidDataSource.setMinIdle(10); // 最小连接数
                druidDataSource.setMaxActive(100);// 最大连接数
                druidDataSource.setPoolPreparedStatements(true);// 开启缓存preparedStatement
                druidDataSource.setUseGlobalDataSourceStat(true);
                // 开启Druid提供的3s慢SQL监控
                Properties properties = new Properties();
                properties.put("druid.stat.mergeSql", true);
                properties.put("druid.stat.slowSqlMillis", 3000);
                druidDataSource.setConnectProperties(properties);
                druidDataSource.setFilters("stat,wall");
                currentDataSources.put(ip, druidDataSource);
                field.set(dynamicPGDataSource, currentDataSources);
                dynamicPGDataSource.afterPropertiesSet();
            }
            DataSourceContext.setPGDataSource(ip);
            ConnectInfo.currentPGIP = ip;
            // 关闭无用连接
            Iterator<Map.Entry<Object, Object>> iterator = currentDataSources.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Object, Object> entry = iterator.next();
                if(!ip.equals(entry.getKey())) {
                    DruidDataSource dataSource = (DruidDataSource)currentDataSources.get(entry.getKey());
                    dataSource.close();
                    iterator.remove();
                }
            }
        } catch (IllegalAccessException | SQLException e) {
            e.printStackTrace();
        }
    }

相关文章:

  • STM32配套程序接线图
  • WPF程序使用AutoUpdate实现自动更新
  • 重学vue3(二):vue3生命周期(包含父与子)
  • 专家系统如何运用谓词逻辑进行更复杂的推理
  • 模版题目的集合
  • maven--依赖的搜索顺序
  • 游戏引擎学习第158天
  • MySQL(第四周)
  • 整本书测试与巩固_《C++并发编程实战》笔记
  • C# 使用Markdown2Pdf把md文件转换为pdf文件
  • Flutter网页交互增强插件pulse_core_web的使用
  • “全志V821:智能玩具的理想之选”——科技赋能,乐趣升级
  • JVM调优关注的核心指标?
  • 《CircleCI:CircleCI:解锁软件开发持续集成(CI)和持续部署(CD)高效密码》
  • c++学习系列----002.写文件
  • Java面试八股—Redis篇
  • 空洞卷积(膨胀卷积)(Dilated Convolution / Atrous Convolution)
  • 【Redis事务】redis中事务的使用
  • kafka rocketmq rabbitmq 都是怎么实现顺序消费的
  • UG的一些操作步骤(自用笔记1)
  • t字型布局的网站在dw怎么做/中国突然宣布大消息
  • 专门做恐怖电影的网站/网络营销的认识与理解
  • 北京易点云是什么公司/seo黑帽技术有哪些
  • wordpress添加背景图/搜狗seo优化
  • 襄阳网站建设首选公司哪家好/关键词优化需要从哪些方面开展?
  • 网上最可靠的保险平台/window优化大师官网