Druid的源码
Druid为alibaba的开源项目,托管在github上。传送门在此 : https://github.com/alibaba/druid
Druid Filter介绍
Druid提供的扩展可以通过查看 druid-xxx.jar!/META-INF/druid-filter.properties 。该文件记录的Filter的对应的别名
druid.filters.default=com.alibaba.druid.filter.stat.StatFilter
druid.filters.stat=com.alibaba.druid.filter.stat.StatFilter
druid.filters.mergeStat=com.alibaba.druid.filter.stat.MergeStatFilter
druid.filters.counter=com.alibaba.druid.filter.stat.StatFilter
druid.filters.encoding=com.alibaba.druid.filter.encoding.EncodingConvertFilter
druid.filters.log4j=com.alibaba.druid.filter.logging.Log4jFilter
druid.filters.log4j2=com.alibaba.druid.filter.logging.Log4j2Filter
druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter
druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.wall=com.alibaba.druid.wall.WallFilter
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter
开始前注意点
以下为Spring-Maven项目的配置
StatFilter 监控统计功能
在xml中配置添加Filter
需要在 Druid 数据库连接池的对应的bean进行配置。(com.alibaba.druid.pool.DruidDataSource) 。
1. 别名配置
可以通过 filters 属性进行配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init">
<!-- 配置StatFilter -->
<property name="filters" value="stat" />
<!--
.... 其他相关的属性配置
-->
</bean>
2. 组合配置
filters 属性可以配置多值
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init">
<!-- 配置StatFilter -->
<property name="filters" value="stat,log4j" />
<!--
.... 其他相关的属性配置
-->
</bean>
3. ProxyFilters属性配置
当需要对Filter进行特定的属性值的配置的时候,可以使用该方式。
<!-- stat Filter Bean配置 -->
<bean id="stat_filter" class="com.alibaba.druid.filter.stat.StatFilter">
<!-- slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis就是慢
slowSqlMillis的缺省值为3000,也就是3秒
logSlowSql 表示通过日志输出执行慢的SQL
slowSqlMillis属性也可以通过connectProperties来配置
<property name="connectionProperties" value="druid.stat.slowSqlMillis=5000">
-->
<property name="slowSqlMillis" value="10000"></property>
<property name="logSlowSql" value="true"></property>
<!-- 合并多个DruidDataSource的监控数据
<property name="useGlobalDataSourceStat" value="true"></property>
-->
<!-- 合并SQL -->
<property name="mergeSql" value="true"></property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init">
<!-- proxyFilters 配置Stat -->
<property name="filters" value="log4j"></property>
<property name="proxyFilters">
<list>
<ref bean="stat_filter" />
</list>
</property>
<!--
.... 其他相关的属性配置
-->
</bean>
StatFilter的相关属性
1. mergeStat 合并SQL
在程序执行中,对于没有参数化的sql,StatFilter的统计效果并不是很好,比如说,以下的3条sql会被统计为3条。然后,实际上以下sql语句在统计的时候是1条。
select * from student where stuId = 2001
select * from student where stuId = 2002
select * from student where stuId = 2003
StatFilter提供了合并的功能。如果设置mergeStat属性为true,则以上的语句都会被合并为
select * from student where stuId = ?
-
使用StateFilter的mergeStat属性配置
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"> <property name="mergeSql" value="true" /> </bean> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> ... ... <property name="proxyFilters"> <list> <ref bean="stat-filter" /> </list> </property> </bean>
-
直接修改StatFilter配置为mergeStatFilter
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init"> <!-- 配置StatFilter --> <property name="filters" value="mergeStat" /> <!-- .... 其他相关的属性配置 --> </bean>
-
通过connectionProperties属性进行配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init"> <!-- 配置StatFilter --> <property name="filters" value="stat" /> <property name="connectionProperties" value="druid.stat.mergeSql=true" /> <!-- .... 其他相关的属性配置 --> </bean>
-
通过添加JVM的参数配置
-Druid.stat.mergeSql=true
-
在druid-0.2.17版本之后,sql合并支持tddl,能够对分表进行合并
2. 慢Sql记录
StatFilter属性slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。slowSqlMillis的缺省值为3000,也就是3秒
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="10000" />
<property name="logSlowSql" value="true" />
</bean>
在上面的配置中,slowSqlMillis被修改为10秒,并且通过日志输出执行慢的SQL。
slowSqlMillis属性也可以通过connectProperties来配置,例如:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="filters" value="stat" />
<property name="connectionProperties" value="druid.stat.slowSqlMillis=5000" />
</bean>
3. 合并多个DruidDataSource的监控数据
缺省多个DruidDataSource的监控数据是各自独立的,在Druid-0.2.17版本之后,支持配置公用监控数据,配置参数为useGlobalDataSourceStat。例如:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="useGlobalDataSourceStat" value="true" />
</bean>
或者通过jvm启动参数来指定,例如:
-Ddruid.useGlobalDataSourceStat=true
全部使用jvm启动参数来配置,可以这样:
-Ddruid.filters=mergeStat -Ddruid.useGlobalDataSourceStat=true
4. 连接泄漏监测
当程序存在缺陷时,申请的连接忘记关闭,这时候,就存在连接泄漏了。Druid提供了RemoveAbandanded相关配置,用来关闭长时间不使用的连接。例如:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="removeAbandoned" value="true" /> <!-- 打开removeAbandoned功能 -->
<property name="removeAbandonedTimeout" value="1800" /> <!-- 1800秒,也就是30分钟 -->
<property name="logAbandoned" value="true" /> <!-- 关闭abanded连接时输出错误日志 -->
</bean>
配置removeAbandoned对性能会有一些影响,建议怀疑存在泄漏之后再打开。在上面的配置中,如果连接超过30分钟未关闭,就会被强行回收,并且日志记录连接申请时的调用堆栈。
5. 保存DruidDataSource的监控记录
DruidDataSource有一个属性timeBetweenLogStatsMillis,配置timeBetweenLogStatsMillis>0之后,DruidDataSource会定期把监控数据输出到日志中。
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="timeBetweenLogStatsMillis" value="300000" />
</bean>
或者通过jvm启动参数来指定,例如:
-Ddruid.timeBetweenLogStatsMillis=300000
6. 定制StatLogger
DruidDataSource是通过com.alibaba.druid.pool.DruidDataSourceStatLoggerImpl.DruidDataSourceStatLoggerImpl来实现输入监控数据到日志的,你可以自定义一个StatLogger,例如:
Java代码
import com.alibaba.druid.pool.DruidDataSourceStatLoggerAdapter;
import com.alibaba.druid.pool.DruidDataSourceStatLogger ;
public class MyStatLogger extends DruidDataSourceStatLoggerAdapter implements DruidDataSourceStatLogger {
... ...
}
配置
<bean id="myStatLogger" class="com.mycompany.MyStatLogger">
... ...
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
... ...
<property name="statLogger" ref="myStatLogger" />
... ...
</bean>
StatViewServlet配置
Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。
这个StatViewServlet的用途包括:
- 提供监控信息展示的html页面
- 提供监控信息的JSON API
注意:使用StatViewServlet,建议使用druid 0.2.6以上版本。
配置Servlet
StatViewServlet是一个标准的javax.servlet.http.HttpServlet,需要配置在你web应用中的WEB-INF/web.xml中。
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
根据配置中的url-pattern来访问内置监控页面,如果是上面的配置,内置监控页面的首页是/druid/index.html
例如:
http://110.76.43.235:9000/druid/index.html
http://110.76.43.235:8080/mini-web/druid/index.html
配置监控页面访问密码
需要配置Servlet的 loginUsername 和 loginPassword这两个初始参数。
具体可以参考: 为Druid监控配置访问权限(配置访问监控信息的用户与密码)
示例如下:
<!-- 配置 Druid 监控信息显示页面 -->
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<!-- 允许清空统计数据 -->
<param-name>resetEnable</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 用户名 -->
<param-name>loginUsername</param-name>
<param-value>druid</param-value>
</init-param>
<init-param>
<!-- 密码 -->
<param-name>loginPassword</param-name>
<param-value>druid</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
配置allow和deny
StatViewSerlvet展示出来的监控信息比较敏感,是系统运行的内部情况,如果你需要做访问控制,可以配置allow和deny这两个参数。比如:
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<param-name>allow</param-name>
<param-value>128.242.127.1/24,128.242.128.1</param-value>
</init-param>
<init-param>
<param-name>deny</param-name>
<param-value>128.242.127.4</param-value>
</init-param>
</servlet>
-
判断规则
deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝。
如果allow没有配置或者为空,则允许所有访问 -
ip配置规则
配置的格式<IP> 或者 <IP>/<SUB_NET_MASK_size>
其中
128.242.127.1/24
24表示,前面24位是子网掩码,比对的时候,前面24位相同就匹配。
-
不支持IPV6
由于匹配规则不支持IPV6,配置了allow或者deny之后,会导致IPV6无法访问。
配置resetEnable
在StatViewSerlvet输出的html页面中,有一个功能是Reset All,执行这个操作之后,会导致所有计数器清零,重新计数。你可以通过配置参数关闭它。
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<param-name>resetEnable</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
配置Druid和Spring的关联监控配置
Druid提供了Spring和Jdbc的关联监控。
按类型拦截配置
<bean id="druid-stat-interceptor"
class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
</bean>
<bean id="druid-type-proxyCreator" class="com.alibaba.druid.support.spring.stat.BeanTypeAutoProxyCreator">
<!-- 所有ABCInterface的派生类被拦截监控 -->
<property name="targetBeanType" value="xxxx.ABCInterface" />
<property name="interceptorNames">
<list>
<value>druid-stat-interceptor</value>
</list>
</property>
</bean>
方法名正则匹配拦截配置
<bean id="druid-stat-interceptor"
class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
</bean>
<bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"
scope="prototype">
<property name="patterns">
<list>
<value>com.mycompany.service.*</value>
<value>com.mycompany.dao.*</value>
</list>
</property>
</bean>
<aop:config>
<aop:advisor advice-ref="druid-stat-interceptor"
pointcut-ref="druid-stat-pointcut" />
</aop:config>
有些情况下,可能你需要配置proxy-target-class,例如:
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="druid-stat-interceptor"
pointcut-ref="druid-stat-pointcut" />
</aop:config>
按照BeanId来拦截配置
<bean id="druid-stat-interceptor"
class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
</bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="proxyTargetClass" value="true" />
<property name="beanNames">
<list>
<!-- 这里配置需要拦截的bean id列表 -->
<value>xxx-dao</value>
<value>xxx-service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>druid-stat-interceptor</value>
</list>
</property>
</bean>