zoukankan      html  css  js  c++  java
  • 一次数据库泄露的解决经历

    前言

    最近用了公司某框架,部署到现场后,现场运维开始维护现场数据,在不断操作的过程中,系统崩溃,查看后台日志,druid连接池已经获取不到连接。于是开始了排查之旅。在此记录。

    排查开始

    首先后台的报错是这样的。

    exception=org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 10, maxActive 10
    

    第一反应maxActive设置的数量太少了。于是改为100。重新启动,并再次操作大量数据。发现过了一段时间100个也满了。

    此时问题不简单了。看来是有代码用了程序连接后,没有释放。

    接下来开始确认原因到底是不是有没有释放。

    在项目中使用的druid连接池。druid连接池是自带图形化监控工具的。于是开始在项目中配置,启动druid连接池。

    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class DruidConfig {
    
       @Bean
       public ServletRegistrationBean statViewServlet(){
          ServletRegistrationBean srb =
             new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
          //设置控制台管理用户
          srb.addInitParameter("loginUsername","root");
          srb.addInitParameter("loginPassword","root");
          //是否可以重置数据
          srb.addInitParameter("resetEnable","false");
          return srb;
       }
    
       /**
        * 注册FilterRegistrationBean
        * @return
        */
       @Bean
       public FilterRegistrationBean druidStatFilter() {
          FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
          //添加过滤规则.
          bean.addUrlPatterns("/*");
          //添加不需要忽略的格式信息.
          bean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
          return bean;
       }
    }
    

    重新启动后,访问:localhost:8081/druid。这个路径有人大概会怀疑如果我是通过网关管理的微服务框架,需要通过网关转发访问吗?其实大可不必,通过网关也可以,也是跳转到直接访问的地址。

    进入访问地址,跳转到如下的登录界面。

    输入刚才在代码中配置的用户名和密码,则可以成功进入。

    我们此时要点击数据源,去关注建立的逻辑连接数和关闭的逻辑连接数,关键的指标在这里,如果连接池的开启和关闭是正常的,那么二者的值应该是相等的。

    再看看此时的活跃连接数

    为0,此时正常,因为还没有进行操作。

    接下来开始对之前的操作进行复现,锁定具体的操作。重复之前现场运维所做操作。

    通过不断的点击功能,缩小功能范围,最终发现,只要点击左侧树,就会造成逻辑打开连接和逻辑关闭次数不一致。

    活跃连接数也到了二者之差。等了几分钟,仍然是这个情况。那么实锤了 这里的代码有问题,连接应该没有释放。那么代码那么多,该如何发现具体代码的位置呢。

    接下来配置druid的abandon策略。通过abandon可以强制回收数据库的连接。而活跃的连接被回收则会打印堆栈信息,这是就知道是哪里的sql代码没有释放了。

    配置如下:

    spring:
      datasource:
        druid:
          remove-abandoned: true
          remove-abandoned-timeout: 30
          log-abandoned: true
    

    重启项目,这个时候durid的监控活跃连接数的功能就可以看到代码信息。

    我们点击如下位置:

    就会弹出上图的堆栈信息。打马赛克的地方就是代码的详细位置,会标记出来。开发人员去响应的类找到相应代码查看即可。

    经过查看代码发现,代码的连接释放存在问题。是自己封装的sql查询类。改为mybatis的写法后,问题解决。

  • 相关阅读:
    团队项目-第一阶段冲刺7
    团队项目-第一阶段冲刺6
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(六) 消息队列篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(四) 配置文件篇
  • 原文地址:https://www.cnblogs.com/jichi/p/13546140.html
Copyright © 2011-2022 走看看