最新项目中用的druid连接数据库遇到一个困扰很久的问题
1 开始用的druid版本是1.1.22版本,由于业务需求,单个连接需要执行很久,理论上不需要用到自动回收,但为了安全,还是加了自动回收,时间设置的2个小时。
随着程序运行,程序经常报The last packet successfully received from the server was XXXXX milliseconds ago. The last packet sent successfully to the server was 0 mill
iseconds ago错误,网上搜索了下答案,有说配置项,改数据库事件设置,试过都没有解决,后续看到https://cloud.tencent.com/developer/article/1397508 分析,觉得有一定道理,就开始后续之路
2.druid包升级到1.2.2,原来的问题是没有了,新的问题出现了,discard long time none received connection,又继续网上搜索答案,出来的结果一塌糊涂,很多说版本回退到1.1.22,心里不由的说wc,这…
有点扯,继续进行搜索测试,修改配置项validationQuery,修改testWhileIdle,修改…继续测试,问题依旧,又搜索到运行时添加druid.mysql.usePingMethod=false,但是没说怎样添加,没办法下载源码进行查看,导入源码后发现如下:
if (valid && isMySql) { // unexcepted branch long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn); if (lastPacketReceivedTimeMs > 0) { long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs; if (lastPacketReceivedTimeMs > 0 // && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { discardConnection(holder); String errorMsg = "discard long time none received connection. " + ", jdbcUrl : " + jdbcUrl + ", version : " + VERSION.getVersionNumber() + ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis; LOG.warn(errorMsg); return false; } } }
这在配置中加timeBetweenEvictionRunsMillis:1800000 就可以了,个人理解是一次操作数据库大于这个时间就会被清除,更直观些就是查询或其他操作在数据库执行时间,这里单位是毫秒。
紧接着查看源码druid.mysql.usePingMethod=false这个设置,既然网上有人说,就看看好使不,源码如下:
configFromProperties(System.getProperties()); } @Override public void configFromProperties(Properties properties) { String property = properties.getProperty("druid.mysql.usePingMethod"); if ("true".equals(property)) { setUsePingMethod(true); } else if ("false".equals(property)) { setUsePingMethod(false); } }
druid加载System.getProperties(),查看属性中的druid.mysql.usePingMethod的对应值,如果false,就不用ping方法,否者用ping方法,进一步查看不用ping方法就是用默认select 1,System.getProperties()查看了下一般是系统的一些参数,但是可以put(key,value),程序启动时间加载进去就可以,项目中用到了定时器(根据自己项目写就可以,加载一次就ok了),就在初始化时间设置了具体值,代码如下:
public void contextInitialized(ServletContextEvent arg0) { try { System.getProperties().put("druid.mysql.usePingMethod", "false"); // 获取Scheduler实例 scheduler = new StdSchedulerFactory().getScheduler(); ...
然后取消timeBetweenEvictionRunsMillis设置进行测试,程序跑1个小时没有任何问题,到此问题解决。
druid个人使用总结:
1.The last packet successfully received from the server was问题升级jar包,我是升级到1.2.2版本
2.discard long time none received connection问题不改程序情况下设置timeBetweenEvictionRunsMillis参数(注意是毫秒),改程序下加System.getProperties().put(“druid.mysql.usePingMethod”, “false”)
druid默认使用usePingMethod方法,此方法并不会更新连接返回时间,导致lastPacketReceivedTimeMs大于timeBetweenEvictionRunsMillis
网上其他的方法感觉要不理解太深,没有给出具体实现,要不就是复制粘贴的,希望对遇到此问题的人有所帮助。
——————————————————–分割线—————————————————————————
本以为到此就ok了,运行了一段时间,日志查看又报The last packet successfully received from the server was XXXX 。。。。
这次崩溃了,看来这个问题不是druid版本的问题,经过了两天的搜索和各种测试,在测试环境下终于100%还原了生产的错误,直接说结果,就是没有解决,重新修改了代码逻辑。
你没有看错,就是没有找到解决方法,接下来说下我测试的各种方法,首先是修改druid配置参数
minIdle=10 validationQuery=select 1 testWhileIdle=true testOnBorrow=false testOnReturn=false keepAlive=true
这些参数的各种组合我基本上都试过来了,我的测试场景是用druid获取连接,执行一次查询,当前线程停N分钟,当N>15分钟时间,以上各种组合都报The last packet successfully received from the server was XXXX错误,只有一种情况例外,就是运行程序和数据库在同一台服务器上,例如连接的数据库是127.0.0.1,并且在网上搜索发现,有人给出过这个解决方案,就是用localhost或者127.0.0.1。但是我所遇到的是在不同服务器上,最后是修改了逻辑,A模块运行完成后关闭服务,等待B模块运行完成后再重新getconnection。
最终的总结和疑问:
1.项目和数据库在同一台服务器上时,可以用127.0.0.1去连接本机数据库避免这个问题的出现。
2.不在同一服务器上时,修改代码逻辑,数据库连接空闲时间控制在10分钟以内不会报这个错误。
疑问:
1.用c3p0测试,同样出现这个问题,这个错误应该是数据库抛出的异常,并非是jar抛出的?
2.druid有keepAlive=true设置,不明白为什么没有去执行,或者执行的效果和我测试想到的不一致?
3.我个人想的是连接空闲10分钟后jar去发送一个select 1的查询,更新下最后包返回的事件,但是这样会出现一个问题,会不会影响实际应用单个执行大于10分钟的应用效果,比如查询4,5张表的关联数据报表,用时20分钟,如果发送select 1去查询,返回的结果是1显然不符合实际业务,但是又没有找到合适的方法去保持连接,由于实际应用没有用到,等到碰到后再进行测试总结,以后有新的进展再进行更新。