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;
    }
  1. 类定义

    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;
     }

    }

  2. 定义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();
         }
     }
相关推荐
Cloud Traveler20 小时前
8.FC平台模块梳理
java·linux·开发语言
0_0梅伊阁诗人21 小时前
Flask
开发语言·数据库·python·flask
失散131 天前
分布式专题——10.2 ShardingSphere-JDBC分库分表实战与讲解
java·分布式·架构·shardingsphere·分库分表
失散131 天前
分布式专题——10.4 ShardingSphere-Proxy服务端分库分表
java·分布式·架构·shardingsphere·分库分表
小兜全糖(xdqt)1 天前
pyspark 从postgresql读取数据
数据库·postgresql
Bellafu6661 天前
spring项目部署后为什么会生成 logback-spring.xml文件
java
递归不收敛1 天前
一、Java 基础入门:从 0 到 1 认识 Java(详细笔记)
java·开发语言·笔记
沐浴露z1 天前
【Java SpringAI智能体开发学习 | 2】SpringAI 实用特性:自定义Advisor,结构化输出,对话记忆持久化,prompt模板,多模态
java·spring·springai
小沈同学呀1 天前
创建一个Spring Boot Starter风格的Basic认证SDK
java·spring boot·后端
码农小伙1 天前
通俗易懂地讲解JAVA的BIO、NIO、AIO
java·nio