官网
https://github.com/alibaba/druid/wiki
介绍 - 《Alibaba Druid v1.0 使用手册》 - 书栈网 · BookStack
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <lombok.version>1.18.8</lombok.version> <logback.version>1.2.3</logback.version>
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency>
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback.version}</version> </dependency>
|
Gradle
1
| compile group: 'com.alibaba', name: 'druid', version: '1.1.16'
|
DruidDataSource 配置
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
配置 |
缺省值 |
说明 |
name |
|
配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:”DataSource-“ + System.identityHashCode (this). 另外配置此属性至少在 1.0.5 版本中是不起作用的,强行设置 name 会出错。详情 - 点此处。 |
url |
|
连接数据库的 url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
username |
|
连接数据库的用户名 |
password |
|
连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用 ConfigFilter。详细看这里 |
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 下建议关闭。 |
maxPoolPreparedStatementPerConnectionSize |
-1 |
要启用 PSCache,必须配置大于 0,当大于 0 时,poolPreparedStatements 自动触发修改为 true。在 Druid 中,不会存在 Oracle 下 PSCache 占用内存过多的问题,可以把这个数值配置大一些,比如说 100 |
validationQuery |
|
用来检测连接是否有效的 sql,要求是一个查询语句,常用 select ‘x’。如果 validationQuery 为 null,testOnBorrow、testOnReturn、testWhileIdle 都不会起作用。 |
validationQueryTimeout |
|
单位:秒,检测连接是否有效的超时时间。底层调用 jdbc Statement 对象的 void setQueryTimeout (int seconds) 方法 |
testOnBorrow |
true |
申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn |
false |
归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。 |
testWhileIdle |
false |
建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。 |
keepAlive |
false (1.0.28) |
连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis,则会执行 keepAlive 操作。 |
timeBetweenEvictionRunsMillis |
1 分钟(1.0.14) |
有两个含义: 1) Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。 2) testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明 |
numTestsPerEvictionRun |
30 分钟(1.0.14) |
不再使用,一个 DruidDataSource 只支持一个 EvictionRun |
minEvictableIdleTimeMillis |
|
连接保持空闲而不被驱逐的最小时间 |
connectionInitSqls |
|
物理连接初始化的时候执行的 sql |
exceptionSorter |
根据 dbType 自动识别 |
当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters |
|
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的 filter:stat 日志用的 filter:log4j 防御 sql 注入的 filter:wall |
proxyFilters |
|
类型是 List<com.alibaba.druid.filter.Filter>,如果同时配置了 filters 和 proxyFilters,是组合关系,并非替换关系 |
数据库密码加密
https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
在命令行中执行如下命令,输出到文件中
1
| java -cp druid-1.0.14.jar com.alibaba.druid.filter.config.ConfigTools csxcgjgmscs > a.txt
|
输出内容
1 2 3
| privateKey:MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA6+4avFnQKP+O7bu5YnxWoOZjv3no4aFV558HTPDoXs6EGD0HP7RzzhGPOKmpLQ1BbA5viSht+aDdaxXp6SvtMQIDAQABAkAeQt4fBo4SlCTrDUcMANLDtIlax/I87oqsONOg5M2JS0jNSbZuAXDv7/YEGEtMKuIESBZh7pvVG8FV531/fyOZAiEA+POkE+QwVbUfGyeugR6IGvnt4yeOwkC3bUoATScsN98CIQDynBXC8YngDNwZ62QPX+ONpqCel6g8NO9VKC+ETaS87wIhAKRouxZL38PqfqV/WlZ5ZGd0YS9gA360IK8zbOmHEkO/AiEAsES3iuvzQNYXFL3x9Tm2GzT1fkSx9wx+12BbJcVD7AECIQCD3Tv9S+AgRhQoNcuaSDNluVrL/B/wOmJRLqaOVJLQGg== publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOvuGrxZ0Cj/ju27uWJ8VqDmY7956OGhVeefB0zw6F7OhBg9Bz+0c84RjzipqS0NQWwOb4kobfmg3WsV6ekr7TECAwEAAQ== password:PNak4Yui0+2Ft6JSoKBsgNPl+A033rdLhFw+L0np1o+HDRrCo9VkCuiiXviEMYwUgpHZUFxb2FpE0YmSguuRww==
|
配置
1 2 3 4 5 6 7 8
| <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:derby:memory:spring-test;create=true" /> <property name="username" value="sa" /> <property name="password" value="${password}" /> <property name="filters" value="config" /> <property name="connectionProperties" value="config.decrypt=true;config.decrypt.key=${publickey}" /> </bean>
|
监控统计配置
参考:Druid 的统计信息、 WebStatFilter、Spring关联监控配置
spring.xml
Spring 监控: Service、Dao 执行情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <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.theking.service.impl.*</value> <value>com.theking.dao.*</value> </list> </property> </bean>
<aop:config> <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut" /> </aop:config>
|
spring-mybatis.xml
SQL 监控、SQL防火墙、慢查询输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"> <property name="mergeSql" value="true" />
<property name="slowSqlMillis" value="5000" /> <property name="logSlowSql" value="true" /> </bean>
<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter"> <property name="statementExecutableSqlLogEnable" value="true" /> </bean> <bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter"> <property name="dbType" value="oracle" /> <property name="logViolation" value="true"/> <property name="throwException" value="false"/> </bean>
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="filters" value="config" /> <property name="proxyFilters"> <list> <ref bean="stat-filter" /> <ref bean="log-filter"/> <ref bean="wall-filter"/> </list> </property> </bean>
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <filter> <filter-name>DruidWebStatFilter</filter-name> <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class> <init-param> <param-name>exclusions</param-name> <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value> </init-param> <init-param> <param-name>sessionStatMaxCount</param-name> <param-value>1000</param-value> </init-param> <init-param> <param-name>sessionStatEnable</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>principalSessionName</param-name> <param-value>sessionusername</param-value> </init-param> <init-param> <param-name>profileEnable</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>DruidWebStatFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<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>
|
logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| <?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <property name="LOG_HOME" value="../logs" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern> </encoder> </appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/ssm_demo-%d{yyyy-MM-dd}.%i.html</FileNamePattern> <MaxHistory>30</MaxHistory> <MaxFileSize>500MB</MaxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%p%d%msg%M%F{32}%L</pattern> </layout> </encoder> </appender>
<appender name="ERROR" class="ch.qos.logback.core.FileAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%p%d%msg%M%F{32}%L</pattern> </layout> </encoder> <file>${LOG_HOME}/error-log.html</file> </appender>
<logger name="com.apache.ibatis" level="TRACE" /> <logger name="java.sql.Connection" level="DEBUG" /> <logger name="java.sql.Statement" level="DEBUG" /> <logger name="java.sql.PreparedStatement" level="DEBUG" />
<root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> <appender-ref ref="ERROR" /> </root>
<appender name="druidSqlRollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/ssm_demo_druidSql-%d{yyyy-MM-dd}.%i.html</FileNamePattern> <MaxHistory>30</MaxHistory> <MaxFileSize>500MB</MaxFileSize> </rollingPolicy> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%p%d%msg%M%F{32}%L</pattern> </layout> </encoder> </appender>
<logger name="druid.sql.Connection" level="INFO" additivity="false"> <appender-ref ref="druidSqlRollingFile"/> </logger> <logger name="druid.sql.DataSource" level="INFO" additivity="false"> <appender-ref ref="druidSqlRollingFile"/> </logger> <logger name="druid.sql.Statement" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> <appender-ref ref="druidSqlRollingFile"/> </logger> <logger name="druid.sql.ResultSet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> <appender-ref ref="druidSqlRollingFile"/> </logger> </configuration>
|
log4j.properties「logback 二选一」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| log4j.rootLogger=error, appendConsole
log4j.logger.druid.sql=error log4j.logger.druid.sql.DataSource=error log4j.logger.druid.sql.Connection=error log4j.logger.druid.sql.Statement=debug log4j.logger.druid.sql.ResultSet=error
log4j.appender.appendConsole=org.apache.log4j.ConsoleAppender
log4j.appender.appendConsole.Threshold=debug
log4j.appender.appendConsole.encoding=UTF-8 log4j.appender.appendConsole.layout=org.apache.log4j.PatternLayout log4j.appender.appendConsole.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.appendDao=org.apache.log4j.FileAppender log4j.appender.appendDao.layout=org.apache.log4j.PatternLayout log4j.appender.appendDao.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}]--[%t] [%p] -%l -%m%n%n log4j.appender.appendDao.Append=false log4j.appender.appendDao.File=dao.txt
log4j.appender.appendService=org.apache.log4j.FileAppender log4j.appender.appendService.layout=org.apache.log4j.PatternLayout log4j.appender.appendService.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}]--[%t] [%p] -%l -%m%n%n log4j.appender.appendService.Append=false log4j.appender.appendService.File=service.txt
|
druid 常见问题
https://blog.csdn.net/maguanghui_2012/article/details/51605330
重复打印日志信息
防御SQL注入攻击
WallFilter的功能是防御SQL注入攻击。它是基于SQL语法分析,理解其中的SQL语义,然后做处理的,智能,准确,误报率低。
Druid 数据库用户密码加密代码实现
明文密码 + 私钥 (privateKey) 加密 = 加密密码
加密密码 + 公钥 (publicKey) 解密 = 明文密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.alibaba.druid.filter.config.ConfigTools; public class DruidTest { public static void main(String[] args) throws Exception { String password = "12345"; System.out.println("密码[ "+password+" ]的加密信息如下:\n"); String [] keyPair = ConfigTools.genKeyPair(512); String privateKey = keyPair[0]; String publicKey = keyPair[1]; password = ConfigTools.encrypt(privateKey, password); System.out.println("privateKey:"+privateKey); System.out.println("publicKey:"+publicKey); System.out.println("password:"+password); String decryptPassword=ConfigTools.decrypt(publicKey, password); System.out.println("decryptPassword:"+decryptPassword); } }
|