Druid线程池帮我们实现了应用程序和数据之间的长连接管理,一个线上变更引起了我的疑问,如果我们数据库切换到备用集群,怎么变更?

数据库连接,一般都是域名连接,现在将域名和IP的绑定关系变了,更新ngix,通过域名能找到新的Ip,然后期望通过新IP连接数据库。此时应用程序中的数据库连接池中还保持老IP的连接,这样会造成新的连接走到新数据库,老的连接走到老数据库。
为了解决这个问题,我们当时的操作是手动将老的数据库kill了,使连接池中的老的连接失效重连到新的IP,这样操作,目标是实现了,但是风险很大,如果kill了老数据库后新的数据库不能用咋办?业务都停了,连接池中的老的连接失效重连,这个过渡时间能持续好久,我们业务是否能接受,这都是风险!

欢迎小伙伴们提供思路!!!

下面是我翻看Druid的清除连接的源码整理出来的几个点,注意看源码中的几处注释

1.DruidDataSource.shrink(boolean checkTime)方法中异步线程 DestroyTask 对无效的Collection做清除 

public void shrink(boolean checkTime) {final List<DruidConnectionHolder> evictList = new ArrayList<DruidConnectionHolder>();try {lock.lockInterruptibly();} catch (InterruptedException e) {return;}try {final int checkCount = poolingCount - minIdle;final long currentTimeMillis = System.currentTimeMillis();for (int i = 0; i < poolingCount; ++i) {DruidConnectionHolder connection = connections[i];if (checkTime) {## 连接不管是否空闲,存活phyTimeoutMillis后强制回收,用于Destroy线程清理连接的时候的检测时间,如果不配置默认等于-1,也就是此处不检查if (phyTimeoutMillis > 0) {long phyConnectTimeMillis = currentTimeMillis - connection.getTimeMillis();if (phyConnectTimeMillis > phyTimeoutMillis) {evictList.add(connection);continue;}}long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis();if (idleMillis < minEvictableIdleTimeMillis) {break;}if (checkTime && i < checkCount) {evictList.add(connection);} else if (idleMillis > maxEvictableIdleTimeMillis) {## 连接的最大存活时间,如果连接的最大时间大于 maxEvictableIdleTimeMillis ,则无视最小连接数强制回收evictList.add(connection);}} else {if (i < checkCount) {evictList.add(connection);} else {break;}}}int removeCount = evictList.size();if (removeCount > 0) {System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);poolingCount -= removeCount;}} finally {lock.unlock();}for (DruidConnectionHolder item : evictList) {Connection connection = item.getConnection();JdbcUtils.close(connection);destroyCount.incrementAndGet();}
}

2.DruidDataSource.public int removeAbandoned() 方法中异步线程 DestroyTask 对无效的Collection做清除 

public int removeAbandoned() {int removeCount = 0;long currrentNanos = System.nanoTime();List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();synchronized (activeConnections) {Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();for (; iter.hasNext();) {DruidPooledConnection pooledConnection = iter.next();if (pooledConnection.isRunning()) {continue;}long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);## 通过datasource.getConnontion() 取得的连接必须在removeAbandonedTimeout这么多秒内调用close()要不我就弄死你.(就是conn不能超过指定的租期);removeAbandonedTimeoutMillis=默认300 * 1000 5分钟if (timeMillis >= removeAbandonedTimeoutMillis) {iter.remove();pooledConnection.setTraceEnable(false);abandonedList.add(pooledConnection);}}}if (abandonedList.size() > 0) {for (DruidPooledConnection pooledConnection : abandonedList) {synchronized (pooledConnection) {if (pooledConnection.isDisable()) {continue;}}JdbcUtils.close(pooledConnection);pooledConnection.abandond();removeAbandonedCount++;removeCount++;.....................}}return removeCount;}

3.DruidDataSource. public DruidPooledConnection getConnecltionDirect(long maxWaitMillis) :检查空闲连接是否有效,如果连接无效关闭连接,再新建一个可用连接

 if (isTestWhileIdle()) {final long currentTimeMillis = System.currentTimeMillis();final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis();final long idleMillis = currentTimeMillis - lastActiveTimeMillis;long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis();if (timeBetweenEvictionRunsMillis <= 0) {timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;}## 获取连接的时候,检查连接的空闲时间,如果空间时间大于配置的时间,检测连接是否还有效,如果连接无效关闭连接,无效连接关闭了后再新建一个可用连接if (idleMillis >= timeBetweenEvictionRunsMillis) {boolean validate = testConnectionInternal(poolableConnection.getConnection());if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(realConnection);continue;}}}

还有其它地方会关闭连接,如果空闲连接数量大于配置的数量,这个时候需要关闭多余的连接。未完待续。。。。。。。。。。

当时我们的操作方案:

回滚方案

       

序号

步骤

操作人

时间

 

7

通过全局锁阻塞新集群数据写入,加锁成功后再进入下一步。
操作命令:
flush tables with read lock ;

DBA

10秒

 

8

记录新集群主库此时binlog的pos点。
操作命令:show master status\G;

DBA

3秒

 

9

新集群主库日志刷盘。
操作命令:flush logs;

DBA

3秒

 

10

解析生成新集群已执行过的dml/ddl语句(解析范围从步骤4的pos起始点开始到步骤9的pos结束点结束)
操作命令:python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uxxx -p'xxx' -dxxx --start-file='mysql-bin.xxxxxx' --start-position=xxx --stop-position=xxx > exec.sql

DBA

以实际执行时间为准

 

11

将sql文件传回原集群主库并执行
操作命令:mysql -uroot -p < exec.sql;

DBA

以实际执行时间为准

 

12

域名进行回切。

DBA

以实际执行时间为准

 

13

迁移完成后验证:
验证应用连接是否正常
验证新集群数据库是否存在连接

DBA
业务运维

10分钟

 
         

注:

其中7到11步骤是为了保证新老集群数据一致性,如果对RTO比较在意,可以容忍小部分数据丢失,建议回退直接从12步开始执行

Druid线程池中的连接什么时候会关闭?相关推荐

  1. java 等待线程池结束_等待线程池中任务执行完毕做优雅关闭

    背景 在Java开发中,如果涉及多线程,会经常使用到线程池,本期不额外讲述线程池本身相关的东西.考虑一种场景,如果我们提交给线程池的任务都相对比较耗时,而在任务启动运行后,如果后续有需求的变更,要重新 ...

  2. druid 线程池监控

    druid 线程池监控 grafana 引入dashboard dashboard编号: 11157 配置 需要单独将 druid 线程池信息收集在 prometheus 中 引入依赖 <dep ...

  3. 但是尚未从池中获取连接_[转载]超时时间已到,但是尚未从池中获取连接!

    估计是链接的人过多,而链接没有释放,你可以考虑再链接字符串中把链接池的数量设大些! 如:"server=localhost;user id=sa;password=;pooling=true ...

  4. C#如何判断线程池中所有的线程是否已经完成(转)

    其 实很简单用ThreadPool.RegisterWaitForSingleObject方法注册一个定时检查线程池的方法,在检查线程的方法内调用 ThreadPool.GetAvailableThr ...

  5. [.Net线程处理系列]专题二:线程池中的工作者线程

    目录: 一.上节补充 二.CLR线程池基础 三.通过线程池的工作者线程实现异步 四.使用委托实现异步 五.任务 六.小结 一.上节补充 对于Thread类还有几个常用方法需要说明的. 1.1 Susp ...

  6. 一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

    一个线程池中的线程异常了,那么线程池会怎么处理这个线程? 参考文章: (1)一个线程池中的线程异常了,那么线程池会怎么处理这个线程? (2)https://www.cnblogs.com/fangua ...

  7. EF 4.1中内部经常提交的 exec sp_reset_connection 的用途原来是为了重用池中的连接...

    sp_reset_connection 的作用 当您使用连接池连接到 SQL Server 时,SQL Server 将调用 sp_reset_connection 存储过程来重用之前池中的连接. ( ...

  8. 【Android 异步操作】线程池 ( Worker 简介 | 线程池中的工作流程 runWorker | 从线程池任务队列中获取任务 getTask )

    文章目录 一.线程池中的 Worker ( 工作者 ) 二.线程池中的工作流程 runWorker 三.线程池任务队列中获取任务 getTask 在博客 [Android 异步操作]线程池 ( 线程池 ...

  9. 线程池中阻塞队列的作用?为什么是先添加列队而不是先创建最大线程?线程池中线程复用原理

    1.一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务.阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使 ...

最新文章

  1. 单片机彩灯移动实验_用S7-1200 PLC实现循环彩灯的控制,含源程序
  2. CVPR 2021 | 超越卷积,自注意力模型HaloNet准确率实现SOTA
  3. 记录,一些jar包的作用
  4. 从0到1建立一张评分卡之模型建立
  5. RvaToFileOffset 内存偏移转成文件偏移(滴水课后作业)
  6. springboot优雅停机
  7. 在大促中什么影响了数据库性能
  8. android compile使用方法,自己创建一个android studio在线依赖compile
  9. linux扩容根目录空间_Linux系统扩容根目录磁盘空间的操作方法
  10. 区块链企业级解决方案 ( Hyperledger )
  11. 用glew,glfw,FreeImage实现opengl学习笔记6坐标变换
  12. vue拦截器刷新登陆页面_vue页面跳转拦截器
  13. 高等数学公式【上册+下册】
  14. 好用的倒计时APP 可以同时开多个倒数计时器的便签
  15. 摄像头云台的设计,组装与使用方法
  16. (转)一位计算机牛人的心得,谈到计算机和数学,很实用
  17. 我的汉字输入法编码方案
  18. 容联携手火星时代教育 促进线上线下一体化
  19. 从程序中学习UKF-SLAM(二)
  20. STM32F105配置为USB设备时

热门文章

  1. 一、JDK 1.8源码分析-源码结构介绍篇
  2. 支付宝小程序身份认证(拉取人脸识别 认证功能 +详细案例)
  3. 802.11 帧(MAC架构)
  4. Windows 10安全指南
  5. STM32F427主控(大疆A板)+K210视觉处理
  6. numpy expand_dims函数
  7. OpenPortal认证系统 界面展示
  8. python数据分析知识体系_数据分析知识体系
  9. java基本数据类型 string,String是基本的数据类型吗?
  10. 我们该如何理解矩估计???