Druid无效链接回收策略(源码分析)(mysql 8小时连接失效问题)

2023-11-03

问题背景(异常Communications link failure)

最近添加了数据库监控后发现会有几十万分之一概率查询失败. 查看日志发现异常如下 :
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
监控如下:
在这里插入图片描述
连接池使用的dbcp 1.4版本. 查了一下同学们讲是mysql 连接如果8小时内持续空闲会被关闭.
通过mysql > show variables like '%timeout%';查到结果确实如此
在这里插入图片描述
随即按照大家讲的将dbcp更换成了druid (1.1.2) 连接池.问题得到解决.另外修改mysql设置也可以好像, 由于集团弹性数据库修改起来流程比较繁琐. 没有深究

druid数据库连接池关键配置说明(注意标红配置)

initialSize: 初始化连接个数
maxActive: 最大连接池数量
minIdle: 最小连接池数量
validationQuery: 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow: false 申请连接时不执行validationQuery检测
testOnReturn: false 归还连接时不执行validationQuery检测
testWhileIdle: true 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测
:timeBetweenEvictionRunsMillis 有两个含义:
1.testWhileIdle的判断依据,详细看testWhileIdle属性的说明 对应以下第一种方式
2.Destroy线程会检测连接的间隔时间 对应以下第二种方式
minEvictableIdleTimeMillis: Destroy worker执行时判断连接空闲时间是否大于 minEvictableIdleTimeMillis, 如果大于判断线程池中空闲连接数是否大于minIdle, 如果大于回收此连接
maxEvictableIdleTimeMillis: Destroy worker执行时判断连接空闲时间是否大于 maxEvictableIdleTimeMillis, 如果大于回收此连接(忽略minIdle).

druid数据库连接池超时连接回收源码分析

第一种方式 : 获取连接时校验

   public DruidPooledConnection getConnection() throws SQLException { //获取连接方法
        return this.getConnection(this.maxWait); 
    }

    public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        this.init();
        if(this.filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return this.getConnectionDirect(maxWaitMillis);
        }
    }
    public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;

        DruidPooledConnection poolableConnection;
        while(true) { //注意这里的循环.直到获取到或者抛出或者break; while循环才会出
            while(true) {
                try {
                    poolableConnection = this.getConnectionInternal(maxWaitMillis);//获取
                    break;
                } catch (GetConnectionTimeoutException var23) {
                    if(notFullTimeoutRetryCnt > this.notFullTimeoutRetryCount || this.isFull()) {
                        throw var23;
                    }

                    ++notFullTimeoutRetryCnt;
                    if(LOG.isWarnEnabled()) {
                        LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                    }
                }
            }

            if(this.testOnBorrow) { 
            	//如果testOnBorrow=true,每次获取连接时都会检查连接有效性.效率较差
                boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if(validate) {
                    break;
                }

                if(LOG.isDebugEnabled()) {
                    LOG.debug("skip not validate connection.");
                }
				//销毁连接(检查有效性结果:无效)
                this.discardConnection(poolableConnection.holder);
            } else if(poolableConnection.conn.isClosed()) {
            	//如果连接已经关闭,销毁
                this.discardConnection(poolableConnection.holder);
            } else {
            	//如果testWhileIdle=false,break;不执行一下校验,连接被返回
                if(!this.testWhileIdle) {
                    break;
                }

                DruidConnectionHolder holder = poolableConnection.holder;
                long currentTimeMillis = System.currentTimeMillis();
                long lastActiveTimeMillis = holder.lastActiveTimeMillis;
                long lastExecTimeMillis = holder.lastExecTimeMillis;
                long lastKeepTimeMillis = holder.lastKeepTimeMillis;
                if(this.checkExecuteTime && lastExecTimeMillis != lastActiveTimeMillis) {
                    lastActiveTimeMillis = lastExecTimeMillis;
                }

                if(lastKeepTimeMillis > lastActiveTimeMillis) {
                    lastActiveTimeMillis = lastKeepTimeMillis;
                }

                long idleMillis = currentTimeMillis - lastActiveTimeMillis;
                long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
                if(timeBetweenEvictionRunsMillis <= 0L) {
                	//默认60000ms,即1分钟
                    timeBetweenEvictionRunsMillis = 60000L;
                }
				
				/*
				1.如果连接空闲时间 < timeBetweenEvictionRunsMillis时间
				2.连接空闲时间 > 0
				不校验,也就是说我们如果通过开启testWhileIdle参数
				校验连接有效性的话timeBetweenEvictionRunsMillis 时间一定不能超过8小时,
				不然依然可能取到失效链接.
				*/
                if(idleMillis < timeBetweenEvictionRunsMillis && idleMillis >= 0L) {
                    break;
                }
				
				//执行校验 (即 : validationQuery中配置的'select 1 from dual'语句)
                boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if(validate) {
                    break;
                }

                if(LOG.isDebugEnabled()) {
                    LOG.debug("skip not validate connection.");
                }

                this.discardConnection(poolableConnection.holder);
            }
        }

        if(this.removeAbandoned) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            poolableConnection.connectStackTrace = stackTrace;
            poolableConnection.setConnectedTimeNano();
            poolableConnection.traceEnable = true;
            this.activeConnectionLock.lock();

            try {
                this.activeConnections.put(poolableConnection, PRESENT);
            } finally {
                this.activeConnectionLock.unlock();
            }
        }

        if(!this.defaultAutoCommit) {
            poolableConnection.setAutoCommit(false);
        }

        return poolableConnection;
    }

第二种方式 : Destroy 定时任务检查需要被回收的连接

	//init方法是线程池创建方法
	public void init() throws SQLException {
		...
		this.createAndLogThread();
	    this.createAndStartCreatorThread();
	    //调用创建销毁线程方法
	    this.createAndStartDestroyThread();
	    this.initedLatch.await();
		...
	}
	//创建销毁线程
	protected void createAndStartDestroyThread() {
        this.destroyTask = new DruidDataSource.DestroyTask();
        if(this.destroyScheduler != null) {
            long period = this.timeBetweenEvictionRunsMillis;
            if(period <= 0L) {
                period = 1000L;
            }
			//启动销毁线程
            this.destroySchedulerFuture = this.destroyScheduler.scheduleAtFixedRate(this.destroyTask, period, period, TimeUnit.MILLISECONDS);
            this.initedLatch.countDown();
        } else {
            String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this);
            this.destroyConnectionThread = new DruidDataSource.DestroyConnectionThread(threadName);
            this.destroyConnectionThread.start();
        }
    }
    //销毁线程
    public class DestroyTask implements Runnable {
       public DestroyTask() {
       }

       public void run() {
       	   //checkTime 为true
           DruidDataSource.this.shrink(true, DruidDataSource.this.keepAlive);
           if(DruidDataSource.this.isRemoveAbandoned()) {
               DruidDataSource.this.removeAbandoned();
           }

       }
   }
   //具体方法
   public void shrink(boolean checkTime, boolean keepAlive) {
        try {
            this.lock.lockInterruptibly();
        } catch (InterruptedException var49) {
            return;
        }

        boolean needFill = false;
        int evictCount = 0;
        int keepAliveCount = 0;
        int fatalErrorIncrement = this.fatalErrorCount - this.fatalErrorCountLastShrink;
        this.fatalErrorCountLastShrink = this.fatalErrorCount;

        int checkCount;
        label956: {
            try {
                if(this.inited) {
                	//可能被销毁数量 = 线程池当前线程数量 - 配置的最小空闲数
                    checkCount = this.poolingCount - this.minIdle;
                    long currentTimeMillis = System.currentTimeMillis();

                    int i;
                    for(i = 0; i < this.poolingCount; ++i) {
                        DruidConnectionHolder connection = this.connections[i];
                        if((this.onFatalError || fatalErrorIncrement > 0) && this.lastFatalErrorTimeMillis > connection.connectTimeMillis) {
                            this.keepAliveConnections[keepAliveCount++] = connection;
                        } else if(checkTime) {
                            long idleMillis;
                            if(this.phyTimeoutMillis > 0L) {
                                idleMillis = currentTimeMillis - connection.connectTimeMillis;
                                if(idleMillis > this.phyTimeoutMillis) {
                                    this.evictConnections[evictCount++] = connection;
                                    continue;
                                }
                            }
							//当前for循环处理的线程空闲时间 = 当前时间 - 连接最后活跃时间
                            idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
                            if(idleMillis < this.minEvictableIdleTimeMillis && idleMillis < this.keepAliveBetweenTimeMillis) {
                                break;
                            }
							//连接空闲时间 >= 配置的最小空闲被回收时间 : minEvictableIdleTimeMillis
                            if(idleMillis >= this.minEvictableIdleTimeMillis) {
                            	/*
                            	checkTime 为方法入参 = true, 
                            	i:当前for循环下标(连接取得时候是取得数组最大坐标,新创建的连接也是放在数组最大坐标上,所以0号坐标一定是最久未使用的那个)
                            	可能被销毁数量 = 线程池当前线程数量 - 配置的最小空闲数(checkCount = this.poolingCount - this.minIdle; )
                            	重点 : 也就是minEvictableIdleTimeMillis配置只会回收超过minIdle的那部分空闲连接
                          		*/
                                if(checkTime && i < checkCount) {
                                    this.evictConnections[evictCount++] = connection;
                                    continue;
                                }
								
								//连接空闲时间 > 配置的最大空闲时间maxEvictableIdleTimeMillis
								//重点 : maxEvictableIdleTimeMillis参数会忽略配置的minIdle
                                if(idleMillis > this.maxEvictableIdleTimeMillis) {
                                    this.evictConnections[evictCount++] = connection;
                                    continue;
                                }
                            }

                            if(keepAlive && idleMillis >= this.keepAliveBetweenTimeMillis) {
                                this.keepAliveConnections[keepAliveCount++] = connection;
                            }
                        } else {
                            if(i >= checkCount) {
                                break;
                            }

                            this.evictConnections[evictCount++] = connection;
                        }
                    }

                    i = evictCount + keepAliveCount;
                    if(i > 0) { //复制有效连接到连接池数组
                        System.arraycopy(this.connections, i, this.connections, 0, this.poolingCount - i);
                        Arrays.fill(this.connections, this.poolingCount - i, this.poolingCount, (Object)null);
                        this.poolingCount -= i;
                    }

                    this.keepAliveCheckCount += keepAliveCount;
                    if(keepAlive && this.poolingCount + this.activeCount < this.minIdle) {
                        needFill = true;
                    }
                    break label956;
                }
            } finally {
                this.lock.unlock();
            }

            return;
        }

        Connection connection;
        DruidConnectionHolder holer;
        if(evictCount > 0) { //销毁刚刚放在数组里的连接
            for(checkCount = 0; checkCount < evictCount; ++checkCount) {
                holer = this.evictConnections[checkCount];
                connection = holer.getConnection();
                JdbcUtils.close(connection);
                destroyCountUpdater.incrementAndGet(this);
            }

            Arrays.fill(this.evictConnections, (Object)null);
        }
        ...
    }

总结(线程回收几种配置方法)

1: 通过配置testOnBorrow=true每次在连接取出时判断, 效率较差
2: 通过配置testWhileIdle=true每次在连接取出时且取出的连接空闲时间超过timeBetweenEvictionRunsMillis时判断,效率较高. 但要注意timeBetweenEvictionRunsMillis的时间一定不能超过8个小时(mysql 自动释放连接时间)
3: 通过配置 timeBetweenEvictionRunsMillisminEvictableIdleTimeMillis定时任务扫空闲线程,超过minEvictableIdleTimeMillis空闲时间的被回收. 缺点:只能回收超出minIdle配置的连接. 另外如果minIdlemaxActive的话, 此方法无效, 相当于没有配置
4: 通过配置timeBetweenEvictionRunsMillismaxEvictableIdleTimeMillis 作为第3种方案的后补方案,但注意timeBetweenEvictionRunsMillis+maxEvictableIdleTimeMillis一定不能>8小时

参考资料

[1]: 《亿级流量网站架构核心技术》 - 张开涛
[2]: druid1.0.21版本源码研究之连接回收(分析解决mysql8小时断线)
[3]: Druid配置参数详解-maxEvictableIdleTimeMillis,minEvictableIdleTimeMillis

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Druid无效链接回收策略(源码分析)(mysql 8小时连接失效问题) 的相关文章

  • SQL UPDATE 语句根据另一个现有行更新列

    基本上我有一个与下表具有相似格式的表格 我想做的是根据这个逻辑更新 Col4 如果 Col2 为空 则用 Col3 更新 Col4 如果 Col2 不为 null 则在 Col1 中查找与 Col2 中的值匹配的值 使用 col3 中的相应
  • 是否可以将新表和旧表从触发器传递到 MySQL 中的过程中?

    是否可以将新表和旧表从触发器传递到 MySQL 中的过程中 我怀疑不会 因为没有过程接受的表这样的数据类型 有什么可能的解决方法吗 理想情况下它看起来像这样 CREATE TRIGGER Product log AFTER UPDATE O
  • 在 Mysql 上使用 EntityManager JPA 运行脚本

    我正在尝试运行脚本 sql 文件 但由于我尝试了多种方法 因此出现多个错误 这是我的主要 sql 脚本 INSERT INTO Unity VALUES 11 paq 0 2013 04 15 11 41 37 Admin Paquete
  • Hibernate + MySQL + rewriteBatchedStatements=true

    我有以下 Hibernate 配置
  • 一个表可以有多个主键吗?

    我现在很困惑 也许你可以帮助我更好地理解这个问题 即一个表可以有两个主键 如果是 那么如何 如果没有 那为什么 您询问是否可以有多个主键field你当然可以 您只能有一个主键 但它可以包含唯一标识行所需的任意数量的列 创建表时使用类似这样的
  • 如何将行变成列?

    我有一个数据库 其中存储分组到项目中的关键字以及与每个关键字相关的数据 然后我显示每个项目的数据网格 每个关键字一行和几列 全部从同一个表 数据 中检索 我有 4 个表 关键字 项目 group keywords 和数据 keywords
  • 如何将 MySQL 查询输出保存到 Excel 或 .txt 文件? [复制]

    这个问题在这里已经有答案了 如何将 MySQL 查询的输出保存到 MS Excel 工作表 即使只能将数据存储在 txt文件 就可以了 From 将 MySQL 查询结果保存到文本或 CSV 文件中 http www tech recipe
  • 从 Grib 天气模型中提取数据

    我已经下载了grib1模型数据来自GFS http en wikipedia org wiki Global Forecast System 我使用的是 Mac OS X 并且能够构建wgrib2文件来自NOAA http en wikip
  • 在 django ORM 中查询时如何将 char 转换为整数?

    最近开始使用 Django ORM 我想执行这个查询 select student id from students where student id like 97318 order by CAST student id as UNSIG
  • 日期时间与时间戳字段

    我是 MySQL 数据库的新手 您是否建议在表创建中使用日期时间或时间戳字段以及原因 我正在使用 MySQL 5 7 和 innodb 引擎 Thanks 我会用TIMESTAMP对于任何需要自动管理的事情 因为它支持诸如ON UPDATE
  • MySQL 将表从 Latin1 转换为 utf8

    我需要将包含大量数据的表从 Latin1 转换为 utf8 以便它可以接受韩语字符 如何更改该表而不损坏其中的数据 我的 SQL 语句是什么 最好的方法是什么 ALTER TABLE database name table name CON
  • 使用连接池后如何处理过多的并发连接?

    Scenario 假设您有一个拥有大量流量的网站或应用程序 即使使用数据库连接池 性能也会受到真正的打击 站点 应用程序甚至可能崩溃 因为并发连接太多 Question 人们有什么选择来处理这个问题 我的想法 我在想有这个问题的人可以创建多
  • MySQL 可选的带有 MATCH 的 LEFT JOIN

    我有以下查询 它对 MySQL Innodb 数据库中同一搜索词的两个不同表中的两列执行全文搜索 SELECT Id MATCH tb1 comment tb2 comment AGAINST search term IN BOOLEAN
  • WHERE NOT EXIST 附近的语法错误

    我在堆栈中搜索 但没有一个达到最终答案 我的查询是这样的 INSERT INTO user username frequence autoSend VALUES feri2 3 1 WHERE NOT EXISTS SELECT FROM
  • 使用“INSERT ... ON DUPLICATE KEY UPDATE”插入多条记录

    我的表结构 table marks 我的目标 我想用条件插入或更新多条记录 我目前正在通过此查询进行检查 第一步 SELECT FROM marks WHERE student 115 AND param 1 第二步 if records
  • MySQL 和 PHP 参数 1 作为资源

    好吧 当我运行下面提到的代码时 PHP 向我抛出此错误 在日志中 Error mysql num rows 期望参数 1 为资源 第 10 行 place 中给出的字符串 9 11号线 queryFP SELECT FROM db coun
  • 将IP保存到数据库中

    当用户登录时 我想将他们的 IP 保存在数据库中 我该怎么做呢 MySQL 字段最适合使用哪种类型 获取IP的PHP代码是什么样的 我正在考虑将其用作登录 会话内容的额外安全功能 我正在考虑使用用户现在拥有的 IP 检查用户从数据库登录的
  • “修改列”与“更改列”

    我知道 我们不能使用重命名列MODIFY COLUMN语法 但我们可以使用CHANGE COLUMN syntax 我的问题是 主要用途是什么modify syntax 例如 ALATER TABLE tablename CHANGE co
  • Tomcat 6找不到mysql驱动

    这里有一个类似的问题 但关于类路径 ClassNotFoundException com mysql jdbc Driver https stackoverflow com questions 1585811 classnotfoundex
  • 无法连接到 MAMP 上的 phpMyAdmin

    我收到此错误消息 MySQL 说道 无法连接 设置无效 phpMyAdmin 尝试连接 MySQL 服务器 但服务器拒绝连接 您应该检查配置中的主机 用户名和密码 并确保它们与 MySQL 服务器管理员提供的信息相对应 用户和通行证是默认的

随机推荐

  • 二维码Data Matrix编码、解码使用举例

    二维码Data Matrix的介绍见 http blog csdn net fengbingchun article details 44279967 这里简单写了个生成二维码和对二维码进行识别的测试例子 如下 int test data
  • 服务器要如何提高性能

    服务器要如何提高性能 一 将服务器虚拟化 如果同期拥有多个项目 增加额外服务器会显得浪费 成本费用也会大幅度上升 这时不妨通过技术将其划分成多个虚拟空间 而每个空间又可以使用不同操作系统 运行不同应用程序 使得符合项目要求 这种方式通常能增
  • Java 多线程 线程8锁(含示例代码)

    以下内容根据视频https www bilibili com video BV1vE411D7KE p 6学习整理 线程8锁 目录 1 标准访问 2 线程休眠 3 新增普通方法 4 两个对象调用两个线程 5 一个对象调用两个静态同步方法 6
  • Unity中加入虚拟按钮不可见

    忙活了一个多小时 按钮老是看不到 调整了半天按钮坐标 最后发现首先将主角 first person control 的x y轴坐标调整到 0 0 Z轴坐标无所谓 再将按钮坐标调整到 0 0 就会在屏幕出现按钮了 此时再随意调整主角坐标 按钮
  • Android OTA 相关工具(八) 使用 lpadd 添加镜像到 super.img

    文章目录 1 lpadd 的编译 2 lpadd 的帮助信息 3 lpadd 的用法 3 1 准备工作 empty 的 super 设备镜像 raw 格式的 super 设备镜像 sparse 格式的 super 设备镜像 3 1 lpad
  • [Android5.1][RK3288] LCD Mipi 调试方法及问题汇总

    调试流程 设置 dts 中的参数 并 配置管脚 背光部分 LCD 初始化序列 cmds 打开 config 检查电压 调试顺序 1 背光有没有亮 2 开机 以及 从休眠状态唤醒 都没有显示内容 3 我碰到的 cmds 问题 问题集锦 我调试
  • (Redis) RDB与AOP持久化的实现与本质区别

    Redis服务器 Redis是一个键值对数据库服务器 服务器中通常包含着任意个非空数据库 而每个非空数据库中又可以包含任意个键值对 我们将服务器中的非空数据库以及它们的键值对统称为数据库状态 例如 下图就是一个包含三个数据库以及数据控中的键
  • 数组扁平化和去重

    数组扁平化和去重 引入题目 已知有数组 1 2 2 3 4 5 5 6 7 8 9 11 12 12 13 14 10 扁平化数组后应该得到的数组为 1 2 2 3 4 5 5 6 7 8 9 11 12 12 13 14 10 一 数组扁
  • Android Studio基础输入文本框EditText

    Android Studio基础输入文本框EditText 接续上一节 Android Studio 基础按钮使用 1 提示文本框 输入文本框 在布局xml编写
  • gitlab-ci docker maven 自动化流水线部署 springboot多模块项目

    一 准备 首先 需要两台服务器 这里为了下面方便理解 我们约定这两台服务器地址 名称和系统 1 gitlab 服务器 服务器A 地址10 10 10 7 内存大于4g不然会一直死 CentOS Linux 7 Core 2 springbo
  • 大白话讲懂word2vec原理和如何使用

    前言 做自然语言处理 Natural Language Processing NLP 这个领域的小伙伴们肯定对word2vec这个模型很熟悉了 它就是一种最为常见的文本表示的算法 是将文本数据转换成计算机能够运算的数字或者向量 在自然语言处
  • 八十二.将整数的奇偶位互换(位运算)

    将整数的奇偶位互换 import java util Scanner public class LianXi public static int exchange int i 和10101010 做与运算取出偶数位 int ou i 0xa
  • IDEF1X

    IDEF系列方法是由美国空军的ICAM Integrated Computer Aided Manufacturing 计划中首次提出一种复杂系统分析与设计方法 它包括了功能建模 IDEF0 信息建模 IDEF1 动态建模 IDEF2 数据
  • 如何测网络稳定性_如何测试无线WiFi系统的信号强度?这篇文章告诉你

    前言 在智能化弱电行业中 我们也会接触无线网络覆盖的项目 比如一个酒店 校园 园区 小区的无线网络覆盖 项目部署实施结束之后 我们怎样才能确定这个无线网络系统符合建设标准了 今天我们一起来聊聊 无线网络信号强度的测试方式 正文 无线网络可通
  • 如何构造大根堆

    如何构造一个大顶堆 C实现 基础知识 堆是一种二叉树结构 但是他的物理保存是一个数组 如下图 实际的保存形式为 5 4 1 25 68 8 1 5 2 3 设每个结点下标为i 则左孩子 2i 1 右孩子 2i 2 最后一个非叶子结点 arr
  • 汇编语言——第13章 int指令

    13 1 int指令 int指令的格式为 int n n为中断类型码 它的功能是引发中断过程 CPU执行int n指令 相当于引发一个n号中断的中断过程 执行过程如下 1 取中断类型码n 2 标志寄存器入栈 IF 0 TF 0 3 CS I
  • c#-中级篇知识合集-part01

    c 中级篇知识合集 part01 001 调试和错误处理 002 中断模式下的调试 003 错误处理 异常处理 004 异常处理 案例2 005 面向对象编程 类 006 类的定义和声明 Program cs Vector3 cs Vehi
  • 小鹏前自动驾驶AI负责人刘兰个川转投安克,研发具身智能

    作者 StrongerTang 编辑 自动驾驶Daily 点击下方卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 本文只做学术分享 如有侵权 联系删文 据新智驾消息 小鹏前自动驾驶AI负责人刘兰个川 Patrick 近期已加入
  • 基于FPGA的混沌信号发生器设计与实现

    提出基于FPGA设计混沌信号发生器的一种改进方法 首先 采用Euler算法 将连续混沌系统转换为离散混沌系统 其次 基于IEEE 754单精度浮点数标准和模块化设计理念 利用Quartus II软件 采用VHDL和原理图相结合的方式设计混沌
  • Druid无效链接回收策略(源码分析)(mysql 8小时连接失效问题)

    目录 问题背景 异常Communications link failure druid数据库连接池关键配置说明 注意标红配置 druid数据库连接池超时连接回收源码分析 第一种方式 获取连接时校验 第二种方式 Destroy 定时任务检查需