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

  • 相关阅读:
    TPS限流
    JDK并发基础与部分源码解读
    tomcat6-servlet规范对接 与 ClassLoader隔离
    tomcat6-输入输出buffer设计
    tomcat6-endpoint设计
    springMVC请求路径 与实际资源路径关系
    mysql 常用的数据类型
    认识IPv4分组
    CSMA/CD协议(载波侦听多路访问/碰撞检测) 最小帧长理解
    简单的vector--- 2
  • 原文地址:https://www.cnblogs.com/tb1152/p/14305752.html
Copyright © 2011-2022 走看看