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

news/2025/3/17 0:37:04/

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

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

AbstractRoutingDataSource
@Primary@Beanpublic 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);// 开启缓存preparedStatementdruidDataSource.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 {@Overrideprotected 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);// 获取当前的 targetDataSourcesMap<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);// 开启缓存preparedStatementdruidDataSource.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();}}


http://www.ppmy.cn/news/1579693.html

相关文章

[人工智能]实现神经网络实例

import numpy as np&#xff1a;导入 NumPy 库&#xff0c;用于数值计算。导入 PyTorch 相关库&#xff1a; import torch&#xff1a;导入 PyTorch 库&#xff0c;深度学习框架核心库。from torchvision.datasets import mnist&#xff1a;从torchvision.datasets中导入 MNIST…

虚幻基础:动画层接口

文章目录 动画层&#xff1a;动画图表中的函数接口&#xff1a;名字&#xff0c;没有实现。动画层接口&#xff1a;由动画蓝图实现1.动画层可直接调用实现功能2.动画层接口必须安装3.动画层默认使用本身实现4.动画层也可使用其他动画蓝图实现&#xff0c;但必须在角色蓝图中关联…

蓝桥杯刷题——第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

一、0握手问题 - 蓝桥云课 算法代码&#xff1a; #include <iostream> using namespace std; int main() {int sum0;for(int i49;i>7;i--)sumi;cout<<sum<<endl;return 0; } 直接暴力&#xff0c;题意很清晰&#xff0c;累加即可。 二、0小球反弹 - 蓝…

我的创作纪念日:730天的技术写作之旅

我的创作纪念日&#xff1a;730天的技术写作之旅 机缘 从一篇案例分析开始 2023年3月13日&#xff0c;我写下了第一篇技术博客《软考高级-系统分析师-案例分析-系统维护与设计模式》。那时的初心很简单&#xff1a; 沉淀实战经验——在备考软考系统分析师时&#xff0c;发现…

Docker-compose一键部署Zabbix监控平台

1. 环境准备 1.1 系统版本 [rootmonitor ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootmonitor ~]# uname -a Linux monitor 3.10.0-1160.108.1.el7.x86_64 #1 SMP Thu Jan 25 16:17:31 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux 1.2 Docker版本…

深搜专题11:分数字

描述 将整数N分成K个整数的和且每个数大于等于A小于等于B&#xff0c;求有多少种分法 注意&#xff1a;5 0 0 0 和 0 5 0 0被视为一种方法 输入描述 输入只有一行&#xff0c;分别输入N,K,A,B (所有数字均为不大于30的非负整数) 输出描述 输出只有一行&#xff0c;即多少种分法…

英语学习(GitHub学到的分享)

【英语语法&#xff1a;https://github.com/hzpt-inet-club/english-note】 【离谱的英语学习指南&#xff1a;https://github.com/byoungd/English-level-up-tips/tree/master】 【很喜欢文中的一句话&#xff1a;如果我轻轻松松的学习&#xff0c;生活的幸福指数会提高很多…

SSL 原理及实验

引言 为了实现远程办公或者远程客户访问内网的资源 &#xff08;1&#xff09;回顾历史&#xff1a; 起初先出现SSL(Secure Sockets Layer&#xff09;&#xff0d;安全套接层协议。 美国网景Netscape公司1994年研发&#xff0c;介于传输层TCP协议和应用层协议之间的一种协议…