天冰

druid discard long time none received connection问题解析

最新项目中用的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显然不符合实际业务,但是又没有找到合适的方法去保持连接,由于实际应用没有用到,等到碰到后再进行测试总结,以后有新的进展再进行更新。

posted on 2021-01-21 14:38  天冰  阅读(4743)  评论(3编辑  收藏  举报

导航