阅读更多

3顶
1踩

研发管理

转载新闻 解Bug之路-Druid的Bug

2017-05-16 10:58 by 副主编 jihong10102006 评论(1) 有7549人浏览
笔者很热衷于解决Bug,同时比较擅长(网络/协议)部分,所以经常被唤去解决一些网络IO方面的Bug。现在就挑一个案例出来,写出分析思路,以飨读者,希望读者在以后的工作中能够少踩点坑。

前言

此Bug是Druid低版本的Bug,此Bug至少在1.0.12版本就已经修复。

Druid的Bug现场

在紧张的新项目开发的日子里,突然收到线上某系统的大量报警,对应系统的人员发现此系统在某一台机器上dump了大量的error日志。日志基本都是:
Druid:  GetConnectionTimeoutException

此系统所有用到数据库的地方都抛出此异常。于是祭出重启大法,重启过后,一切Okay。然后对应的系统人员开始排查这个问题,一直没有结果。
过了两天,又收到此类型的error日志报警,而且这一次是有两台系统同时爆出此种错误。紧急重启后,将此问题紧急报到我们这边处理。鉴于本人有丰富的IO处理经验,当然落到了本人头上。

Bug复盘

此系统是通过Druid连接后面的数据库分库分表Proxy,再由此Proxy连接后面的数据库。示意图如下所示:

缩小Bug范围

获取连接超时(GetConnectionTimeoutException)此错误的出现,只有两种可能:

1.业务系统本身Druid获取连接失败。   
2.作为中间件的Sharding Proxy获取连接失败。

在这个Bug里面很明显是Druid创建连接失败,原因如下:

1.此系统有10多台机器,仅仅有两台出现此种故障。   
2.这两台重启后一切正常。

如果说这两台是由于同机房问题出现统一的网络连接异常,那么并不能解释重启后正常这一现象。

Druid问题定位

于是开始分析为何获取连接超时,第一步当然是开始寻找源码中日志抛出异常点。上源码:
DruidConnectionHolder holder;
.......
try {
    if (maxWait > 0) {
       holder = pollLast(nanos);
    }
    else {
       holder = takeLast();
    }
    ......
}finally {
    lock.unlock();
}
if(holder == null){
   ......
    if (this.createError != null) {
        throw new GetConnectionTimeoutException(errorMessage, createError);
    } else {
        throw new GetConnectionTimeoutException(errorMessage);
    }   
}

可见,这边获取到的DruidConnectionHolder为null,则抛出异常。

Druid获取连接的过程

在分析这个问题之前,先得看下Druid是如何创建连接的,下面是本人阅读Druid源码后画的示意图:

可见druid创建连接都是通过一个专门的线程来进行的,此图省略了大量的源码细节。

为何Holde为null?

继续上源码
private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {
    for (;;) {
        if (poolingCount == 0) {
            emptySignal(); // send signal to CreateThread create connection

            if (estimate <= 0) {
                waitNanosLocal.set(nanos - estimate);
                return null;
            }
            ...
            try {
                long startEstimate = estimate;
                estimate = notEmpty.awaitNanos(estimate); 
                ......
            } finally {
              ......
            }
            ......
            if (poolingCount == 0) {
                if (estimate > 0) {
                    continue;
                }
                waitNanosLocal.set(nanos - estimate);
                return null;
            }
        }
        decrementPoolingCount();
        DruidConnectionHolder last = connections[poolingCount];
        connections[poolingCount] = null;
        return last;
    }
}

可见,如果触发条件,estimate<=0,则返回null。
上述源码的过程示意图如下:

继续追踪

由此可见,在获取连接的时候一直超时,不停的爆GetConnectionTimeoutException异常,明显是由于创建连接线程出了问题。那到底除了什么问题呢?由于Druid的代码比较多,很难定位问题点,于是还从日志入手。

进一步挖掘日志

错误信息量最大的是最初出现错误的时间点,这是笔者多年排查错误的经验总结。由于正好有两台出错,比较其错误的共同点也能对解决问题大有裨益。
大量create connection error

当笔者和同事追查错误的源头的时候,在有大量的create connection error,奇怪的是,到了一个时间点之后,就只剩GetConnectionTimeoutException异常了。
继续分析,在出现create connection error的时候,还是有部分业务请求能够执行的。即获取到了连接。
翻了翻源码,create connection error是在创建连接线程打印的,如果这个异常没有继续打印,而同时连接也获取不到,就在很大程度上表明:

创建连接根本就是被销毁了!

错误分界点

于是开始寻找什么时候create connection error开始销毁。于是通过笔者和同事在无数的错误日志中用肉眼发现了一个不寻常的日志隐蔽在大量的错误日志间:

Druid:create holder error


在这个错误出现之后,就再也没有了create connection error,翻了翻另一台机器的日志,页是同样的现象!

源码寻找Bug

隐隐就感觉这个日志是问题错误的关键,打出这个日志的源码为:
创建连接线程
@Override
public void run() {
            runInternal();
}

private void runInternal() {

    for (; ; ) {
        ......
        try{
            holder = new DruidConnectionHolder(DruidDataSource.this,connection);
        }catch(SQLException ex){
            LOG.error("create connection holder error",ex);
            // 这句break就是罪魁祸首
            // 至少在1.0.12修复
            break;
        }
        ......
    }
}

这边竟然在for(;;)这个死循环中break了!!!,那么这个线程也在break后跳出了死循环从而结束了,也就是说创建连接线程被销毁了!!!如下图所示:

为何create holder error?

继续搜寻日志,发现create holder error之前,会有获取事务隔离级别的报错。那么结合源码,错误的发生如下图:

即在Druid的创建连接线程创建连接成功之后,还需要拿去数据库的holdability,isolation(隔离级别)等MetaData,在获取MetaData失败的时候,则会抛出异常,导致创建连接线程Break。
但是如果在handshake的时候就失败,那么由于Druid处理了这种异常,打印create connection error,并继续创建连接。
于是就有了在create holder error之前大量create connection error,在这之后没有了的现象。

完结了么?

Druid的Bug是弄清楚了,但是为何连接如此不稳定,有大量的创建连接异常,甚至于Druid前脚创建连接成功,后脚发送命令就失败呢?

Sharding Proxy的Bug

于是此问题又萦绕在笔者心头,在又一番不下于上述过程的努力之后,发现一个月之前上线的新版本的Sharding Proxy的内存泄露Bug导致频繁GC(并定位内存泄露点),导致了上述现象,如下图所示:

由于每次内存泄露过小,同时Sharding Proxy设置的内存过大。所以上线后过了一个月才有频繁的GC现象。之前上线后,大家观察了一周,发现没有任何异常,就不再关注。与此类似,如果DB负载过高的话,笔者推测也会触发Druid的Bug。

最后处理

笔者去翻Druid最新源码,发现此问题已经修复,紧急联系各业务线升级Druid,同时让Sharding Proxy负责人修改了最新代码并上线。
终于这次的连环Bug算是填完了。

总结

追查Bug,日志和源码是最重要的两个部分。最源头的日志信息量最大,同时要对任何不同寻常的现象都加以分析并推测,最后结合源码,才能最终找出Bug。
  • 大小: 76.6 KB
  • 大小: 237.7 KB
  • 大小: 144.8 KB
  • 大小: 42.9 KB
  • 大小: 57 KB
  • 大小: 139 KB
3
1
评论 共 1 条 请登录后发表评论
1 楼 西蜀石兰 2017-05-17 15:40
涨见识了哈,可能用不到这个插件,但拓展了解决bug的思路和方法

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 解Bug之路-串包Bug

    解Bug之路-串包Bug 笔者很热衷于解决Bug,同时比较擅长(网络/协议)部分,所以经常被唤去解决一些网络IO方面的Bug。现在就挑一个案例出来,写出分析思路,以飨读者,希望读者在以后的工作中能够少踩点坑。 串包Bug...

  • 解Bug之路-主从切换”未成功”?

    解Bug之路-主从切换”未成功”?前言数据库主从切换是个非常有意思的话题。能够稳定的处理主从切换是保证业务连续性的必要条件。今天笔者就来讲讲主从切换过程中一个小小的问题。故障场景最近线上进...

  • 【转】解Bug之路-串包Bug

    笔者很热衷于解决Bug,同时比较擅长(网络/协议)部分,所以经常被唤去解决一些网络IO方面的Bug。现在就挑一个案例出来,写出分析思路,以飨读者,希望读者在以后的工作中能够少踩点坑。 串包Bug现场 前置故障Redis超时...

  • druid keepAlive bug踩坑

    druid 1.2.5及以下版本 连接保活bug踩坑

  • Druid核心源码解析--DruidDataSource

    } // bug fixed for dead lock, for issue #2980 DruidDriver.getInstance(); /**控制创建移除连接的锁,并且通过条件去控制一个连接的生成消费**/ // public DruidAbstractDataSource(boolean lockFair){ // lock ...

  • 解Bug之路-主从切换“未成功“?

    这让对应应用的开发百思不得其解,于是求助了笔者一探究竟。 怎么发现的 应用开发收到Cat监控告警,发现这个应用(A)中的请求在好几台机器中一直稳定失败。联想到昨晚刚做过数据库主从切换演练,于是上机器netstat -...

  • SpringBoot-Druid

    拦截器和过滤器用到了servlet,所以要注册servlet 拦截器主要拦截以/druid/开头的 最后使用localhost://8080/就可以访问监控平台。

  • druid源码解读--druid连接池init方法源码解读

    Druid连接池init方法源码解读 今日主要针对druid连接池中的Data Source核心代码中的初始化方法进行源码解读和学习。尤其针对init方法中的createAndStartCreatorThread/createAndStartDestroyThread线程进行详细的...

  • druid开发步骤(部分配置bug解决)

    1.新建Maven web_app项目,名为xxx_druid,在pom.xml中引入druid-1.2.8和mysql-connector-8.0.28以及相关依赖。 pom.xml相关依赖: &lt;dependencies&gt; &lt;dependency&gt; &lt;groupId&gt;com.alibaba&lt;...

  • druid 连接不上mysql_druid-mysql-mybatis-connection - druid 获取不到连接

    org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for ... nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis ...

  • springboot下使用druid-spring-boot-starter

    Druid声称是Java语言中最好的数据库连接池。spring boot start自动装配组件,简化组件使用,所以Druid也推出了druid-spring-boot-starter。 1.pom.xmlyi

  • 多数据源切换-Druid

    * @bug [nothing] * @see [nothing] * @备注: */ public class LoadDataSource extends AbstractHibernateDao { protected final Log logger = LogFactory.getLog(getClass()); private BaseExtendDao ...

  • 记录压测时阿里druid获取数据源连接阻塞的bug

    查找到druid mysqlUtis类后发现。出现阻塞原因在于版本不匹配,低版本的driud加载的是com.mysql.jdbc.MySQLConnection,而8.0版本的mysql-connector用的是com.mysql.cj.jdbc.ConnectionImpl。升级druid到1.2以上版本...

  • python源码基于YOLOV5安全帽检测系统及危险区域入侵检测告警系统源码.rar

    本资源提供了一个基于YOLOv5的安全帽检测系统及危险区域入侵检测告警系统的Python源码 该系统主要利用深度学习和计算机视觉技术,实现了安全帽和危险区域入侵的实时检测与告警。具体功能如下: 1. 安全帽检测:系统能够识别并检测工人是否佩戴安全帽,对于未佩戴安全帽的工人,系统会发出告警信号,提醒工人佩戴安全帽。 2. 危险区域入侵检测:系统能够实时监测危险区域,如高空作业、机械设备等,对于未经授权的人员或车辆进入危险区域,系统会立即发出告警信号,阻止入侵行为,确保安全。 本资源采用了YOLOv5作为目标检测算法,该算法基于深度学习和卷积神经网络,具有较高的检测精度和实时性能。同时,本资源还提供了详细的使用说明和示例代码,便于用户快速上手和实现二次开发。 运行测试ok,课程设计高分资源,放心下载使用!该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能,如增加多种安全帽和危险区域的识别、支持多种传感器数据输入、实现远程监控等。

  • 基于SpringBoot的响应式技术博客的设计和实现(源码+文档)

    本课题将许多当前比较热门的技术框架有机的集合起来,比如Spring boot、Spring data、Elasticsearch等。同时采用Java8作为主要开发语言,利用新型API,改善传统的开发模式和代码结构,实现了具有实时全文搜索、博客编辑、分布式文件存贮和能够在浏览器中适配移动端等功能的响应式技术博客。 本毕业设计选用SpringBoot框架,结合Thymeleaf,SpringData,SpringSecurity,Elasticsearch等技术,旨在为技术人员设计并实现一款用于记录并分享技术文档的技术博客。通过该技术博客,方便技术人员记录自己工作和学习过程中的点滴,不断地进行技术的总结和积累,从而提升自己的综合能力,并通过博客这一平台,把自己的知识、经验、教训分享给大家,为志同道合者提供一个相互交流、共同学习的平台,促使更多的人共同进步[9]。学习到别人的一些良好的设计思路、编码风格和优秀的技术能力,使笔者的设计初衷。本系统主要面向web端的用户,希望能给用户更多的学习和交流的选择。

  • javalab 3.zip

    javalab 3.zip

  • J0001基于javaWeb的健身房管理系统设计与实现

    该系统基于javaweb整合,数据层为MyBatis,mysql数据库,具有完整的业务逻辑,适合选题:健身、健身房、健身房管理等 健身房管理系统开发使用JSP技术和MySQL数据库,该系统所使用的是Java语言,Java是目前最为优秀的面相对象的程序设计语言,只需要开发者对概念有一些了解就可以编写出程序,因此,开发该系统总体上不会有很大的难度,同时在开发系统时,所使用的数据库也是必不可少的。开发此系统所使用的技术都是通过在大学期间学习的,对每科课程都有很好的掌握,对系统的开发具有很好的判断性。因此,在完成该系统的开发建设时所使用的技术是完全可行的。 学员主要实现的功能有:网站信息、课程信息、教练列表、我的信息、登录 员工主要实现的功能有:工资查询、会员管理、器材借还、健身卡管理、个人中心、登录 教练主要实现的功能有:工资查询、学员列表、个人中心 管理员是系统的核心,可以对系统信息进行更新和维护,主要实现的功能有:个人中心、学员管理、教练管理、网站信息管理、器械信息管理、课程信息管理。

  • 架构.cpp

    架构.cpp

  • 利用Python实现中文文本关键词抽取(三种方法)

    文本关键词抽取,是对文本信息进行高度凝练的一种有效手段,通过3-5个词语准确概括文本的主题,帮助读者快速理解文本信息。目前,用于文本关键词提取的主要方法有四种:基于TF-IDF的关键词抽取、基于TextRank的关键词抽取、基于Word2Vec词聚类的关键词抽取,以及多种算法相融合的关键词抽取。笔者在使用前三种算法进行关键词抽取的学习过程中,发现采用TF-IDF和TextRank方法进行关键词抽取在网上有很多的例子,代码和步骤也比较简单,但是采用Word2Vec词聚类方法时网上的资料并未把过程和步骤表达的很清晰。因此,本文分别采用 1. TF-IDF方法 2. TextRank方 3. Word2Vec词聚类方法 实现对专利文本(同样适用于其它类型文本)的关键词抽取,通过理论与实践相结合的方式,一步步了解、学习、实现中文文本关键词抽取。

Global site tag (gtag.js) - Google Analytics