一文读懂 druid连接池

           起因:

           最近微服务项目组新增了一个工程ttas,可是项目上线以来每天都会出现关于ttas超时响应的监控告警。

接口整整耗时26秒,由于微服务超时时间设置为2秒,所以响应超时。

         问题分析:

         通过对日志的分析发现接口的耗时全部集中在数据库连接的获取和销毁上,因此初步推断是数据库连接池的问题,项目组连接池组件用的是阿里的druid组件,配置如下:

         

  type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: oracle.jdbc.driver.OracleDriver
    filters: stat
    pool-prepared-statements: true
    max-open-prepared-statements: 20 
    initial-size: 20	######初始化连接
    ######最大空闲连接
    max-idle: 50
    ######最小空闲连接
    min-idle: 50
    ######最大连接数量
    max-active: 80
    ######是否在自动回收超时连接的时候打印连接的超时错误
    log-abandoned: true
    ######是否自动回收超时连接
    remove-abandoned: true
    ######超时时间(以秒数为单位)
    remove-abandoned-timeout: 180
    ######超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 
    max-wait: 60000
    test-while-idle: true
    ######检测数据库的查询语句
    validation-query: select 1 from dual
    test-on-borrow: true
    ######每隔五分钟检测空闲超过10分钟的连接
    min-evictable-idle-time-millis: 300000
    time-between-eviction-runs-millis: 60000

  讲道理,在问题没有出现之前,对这些配置属性的了解也仅仅是停留在字面上的意思,并且配置的设置也是一个copy的过程。因此接着这个问题的契机,需要好好深入的了解下配置属性的真实意思以及关联属性。

       

配置 缺省值 说明
name   配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
jdbcUrl   连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username   连接数据库的用户名
password   连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName   根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle   最小连接池数量
maxWait   获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery   用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis   有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun   不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis    
connectionInitSqls   物理连接初始化的时候执行的sql
exceptionSorter   根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters   属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters   类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

        连接池动态变化过程:

       1:连接池初始化时会自动创建initialSize个连接待用。

       2:当任务越来越多申请连接的线程超过了  initialSize时,连接池会继续创建连接供调用,当数量达到  maxActive时不再创建连接,而是等待maxWait毫秒时间,当再这个时间内还未获取到连接则报错。

       3:当任务高峰逐渐过去,连接也被逐渐归还给线程池,当testWhileIdle=true时,这是每隔timeBetweenEvictionRunsMillis时间会执行validationQuery检测连接是否有效,无效则销毁连接,直到连接数会到minIdle或者都是活跃连接。

 

       通过上述知识储备以及结合项目的配置文件,按照道理项目不会出现连接池的泄露问题,于是上机器查看项目对数据库的连接数,发现只有2个,这个没道理啊,按照durid源码以及文档的说明,最少应该保持minldle个就是20个,怎么只有两个了呢?结合日志发现每次去获取连接然后发现连接是无效的,关闭的时候发生异常。

        问题:是谁强制关闭了连接?

       分析:告警是每隔一段时间出现,而且是业务不活跃的时候才会出现!

       因此会不会是防火墙强制关闭了连接池不活跃的连接呢?顺着这个思路继续追查果然发现了问题,

我们的数据库部署在101网段,而服务则部署在102网段因此中间的防火墙会强制关闭超过30分装不活跃的连接。既然问题已经找到那么该怎么解决?

       最直接的 1:修改防火墙策略,可是防火墙策略不止针对数据库连接而是针对所有的TCP协议连接,因此不到万不得已不用此法。

      迂回战术 2:将timeBetweenEvictionRunsMillis时间设置的尽可能小,让其频繁发送validationQuery查询,让防火墙认为其hu活跃。

     durid方案 3:durid 1.1.10版本后支持心跳检测配置,将keepAlive=true配置,会每隔一段时间向数据库发送心跳检测请求以保证最新连接的活跃。

 

      最终项目采用方案3解决问题,亲测有效!

你可能感兴趣的:(高并发)