在上一篇文章《Spring Boot (三): ORM 框架 JPA 与链接池 Hikari》 咱们介绍了 JPA 与链接池 Hikari 的整合使用,在国内使用比较多的链接池还有一个是阿里开源的 Druid 。本篇文章咱们就来聊一聊 Druid 的一些使用姿式。css
咱们先来看一下官方的回答:java
Druid 是 Java 语言中最好的数据库链接池。 Druid 可以提供强大的监控和扩展功能。mysql
说 Druid 是 Java 语言中最好的数据库链接池,这个笔者我的以为有些吹牛了,至少在性能上和咱们上一篇介绍的 Hikari 是没得比的,相关的性能测试在网上能找到不少,笔者这边就不列举了。可是 Druid 在其余的一些方面就作的比较出色了,功能很是丰富:git
目前 Druid 官方为咱们提供了两种使用依赖方式,一种是基于传统 Java 工程提供的依赖包, maven 坐标以下:github
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
复制代码
还有一种是基于 Spring Boot 提供的依赖包, maven 坐标以下:web
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
复制代码
下面的这种依赖包除了包含了上面的那种 Druid 基础包,还包含了 Spring Boot 自动配置的依赖包以及 sl4j-api ,咱们在 Spring Boot 中使用 Druid ,固然是推荐各位读者使用第二种方式引入依赖。spring
父工程 pom.xml 以下:sql
代码清单:spring-boot-jpa-druid/pom.xml数据库
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springcloud</groupId>
<artifactId>spring-boot-jpa-druid</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-jpa-druid</name>
<description>spring-boot-jpa-druid</description>
<properties>
<druid.version>1.1.20</druid.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
复制代码
druid-spring-boot-starter
,版本号为 1.1.20。代码清单:spring-boot-jpa-druid/src/main/resources/application-pass.ymlapache
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 链接池的配置信息
# 初始化时创建物理链接的个数
initial-size: 3
# 链接池最小链接数
min-idle: 3
# 链接池最大链接数
max-active: 20
# 获取链接时最大等待时间,单位毫秒
max-wait: 60000
# 申请链接的时候检测,若是空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测链接是否有效。
test-while-idle: true
# 既做为检测的间隔时间又做为testWhileIdel执行的依据
time-between-connect-error-millis: 60000
# 销毁线程时检测当前链接的最后活动时间和当前时间差大于该值时,关闭当前链接
min-evictable-idle-time-millis: 30000
# 用来检测链接是否有效的sql 必须是一个查询语句
# mysql中为 select 'x'
# oracle中为 select 1 from dual
validation-query: select 'x'
# 申请链接时会执行validationQuery检测链接是否有效,开启会下降性能,默认为true
test-on-borrow: false
# 归还链接时会执行validationQuery检测链接是否有效,开启会下降性能,默认为true
test-on-return: false
# 是否缓存preparedStatement,mysql5.5+建议开启
pool-prepared-statements: true
# 当值大于0时poolPreparedStatements会自动修改成true
max-pool-prepared-statement-per-connection-size: 20
# 合并多个DruidDataSource的监控数据
use-global-data-source-stat: false
# 配置扩展插件
filters: stat,wall,slf4j
# 经过connectProperties属性来打开mergeSql功能;慢SQL记录
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 定时输出统计信息到日志中,并每次输出日志会致使清零(reset)链接池相关的计数器。
time-between-log-stats-millis: 300000
# 配置DruidStatFilter
web-stat-filter:
enabled: true
url-pattern: '/*'
exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
# 配置DruidStatViewServlet
stat-view-servlet:
# 是否启用StatViewServlet(监控页面)默认值为false(考虑到安全问题默认并未启动,如需启用建议设置密码或白名单以保障安全)
enabled: true
url-pattern: '/druid/*'
# IP白名单(没有配置或者为空,则容许全部访问)
allow: 127.0.0.1,192.168.0.1
# IP黑名单 (存在共同时,deny优先于allow)
deny: 192.168.0.128
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登陆名
login-username: admin
# 登陆密码
login-password: admin
复制代码
time-between-log-stats-millis
输出至日志中,合并多个DruidDataSource的监控数据 use-global-data-source-stat
不可开启,不然启动会报错。spring.datasource.druid.filters
:由于 Druid 的扩展是经过 Filter 插件的形式来开启的,这里咱们开启了 stat
和 wall
,这俩个分别为监控和防护 SQL 注入攻击。 Druid 还提供了一些其余默认的 Filter ,以下表:Filter类名 | 别名 |
---|---|
default | com.alibaba.druid.filter.stat.StatFilter |
stat | com.alibaba.druid.filter.stat.StatFilter |
mergeStat | com.alibaba.druid.filter.stat.MergeStatFilter |
encoding | com.alibaba.druid.filter.encoding.EncodingConvertFilter |
log4j | com.alibaba.druid.filter.logging.Log4jFilter |
log4j2 | com.alibaba.druid.filter.logging.Log4j2Filter |
slf4j | com.alibaba.druid.filter.logging.Slf4jLogFilter |
commonlogging | com.alibaba.druid.filter.logging.CommonsLogFilter |
wall | com.alibaba.druid.wall.WallFilter |
从名称上能够看出来,主要是一些编码和日志的相关 Filter 。
在生产环境中,直接在配置文件中暴露明文密码是一件很是危险的事情,出于两点考虑:对外,即便应用服务被入侵,数据库仍是安全的;对内,生产环境的数据库密码理论上应该只有 dba 知道,可是代码都是在代码仓库中放着的,若是密码没有加密,每次发布前 dba 都须要手动修改配置文件后再进行打包编译。
首先,咱们须要生成数据库密码的密文,须要在命令行中执行以下命令:
java -cp druid-1.0.16.jar com.alibaba.druid.filter.config.ConfigTools you_password
复制代码
输出以下:
privateKey:MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAh12hnaZuMe76Yb4pi7ogSAEMOcavmz7Blo8DYxeipxeZQhnrXngxc0gAQ6ORlofLWtDm6S7bI7wfDT2EFy/2DwIDAQABAkABMRjYK3vy4pi/vY3eFhBssd2qsI4hPsczjSTJfY7IC9Dc1f7g0axTM6Cx68tRUwv0rSnUiJ5EcDEhuD0JusSZAiEAwX1HpCTq8QgBV1WriHQC7Cd/9Qqp1V4yJeA/jdvXhbsCIQCzGS6wdTQCXDZKLvjRLeSUyTmmIqV/wckqdnpMUZ2BvQIgBIamr1tBt6OlTGKvoYB9NQLzhkrakCgk6ifltK7IytMCIBIbf67zipiafhqt+RYdD7lDRwLXCeiKzS3v4JmKvuP5AiEAr+zqD6sdXv7rWjqu50n+LXbWtNP/M4JzzO1mJOHEhoE=
publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIddoZ2mbjHu+mG+KYu6IEgBDDnGr5s+wZaPA2MXoqcXmUIZ6154MXNIAEOjkZaHy1rQ5uku2yO8Hw09hBcv9g8CAwEAAQ==
password:Y464AerH8tabxQg5DlkUej6gQ64KY73ahgiPyaB0vguLBLjUEEkVu6VBueiXxcnMfVjh1Nbd+lJNUTnS1a3/xg==
复制代码
这里咱们须要将生成的公钥 publicKey
和密码 password
加入配置文件中, application-decrypt.yml
以下:
代码清单:spring-boot-jpa-druid/src/main/resources/application-decrypt.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
# 加密后密文,原密码为 123456
password: Y464AerH8tabxQg5DlkUej6gQ64KY73ahgiPyaB0vguLBLjUEEkVu6VBueiXxcnMfVjh1Nbd+lJNUTnS1a3/xg==
driverClassName: com.mysql.cj.jdbc.Driver
druid:
filter:
config:
enabled: true
connection-properties: config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIddoZ2mbjHu+mG+KYu6IEgBDDnGr5s+wZaPA2MXoqcXmUIZ6154MXNIAEOjkZaHy1rQ5uku2yO8Hw09hBcv9g8CAwEAAQ==
# 剩余配置省略
复制代码
application.yml
以下:代码清单:spring-boot-jpa-druid/src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: spring-boot-jpa-druid
profiles:
active: decrypt
jpa:
database: mysql
show-sql: true
generate-ddl: true
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
复制代码
其他的测试代码同上一篇文章《Spring Boot (三): ORM 框架 JPA 与链接池 Hikari》,有兴趣的读者能够访问 Github 仓库获取,笔者这里就不一一列举了。
咱们在主配置文件中,选择密码加密的配置文件启动,将 spring.profiles.active
配置为 decrypt
,点击启动,能够看到工程正常启动,查看控制台输出日志,其中有这么一句:
2019-09-22 21:21:54.501 INFO 16972 --- [-Log-1465691120] c.a.d.p.DruidDataSourceStatLoggerImpl : {"url":"jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false","dbType":"mysql","name":"DataSource-1465691120","activeCount":0,"poolingCount":3,"poolingPeak":3,"poolingPeakTime":"2019-09-22 21:21:54","connectCount":0,"closeCount":0,"physicalConnectCount":3}
复制代码
能够看到,咱们配置的监控信息输出会在系统启动的时候先输出一次,咱们在配置文件中配置的是每5分钟输出一次,等十分钟看一下控制台的输出信息,结果以下:
2019-09-22 21:26:54.503 INFO 16972 --- [-Log-1465691120] c.a.d.p.DruidDataSourceStatLoggerImpl : {"url":"jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false","dbType":"mysql","name":"DataSource-1465691120","activeCount":0,"activePeak":1,"activePeakTime":"2019-09-22 21:21:54","poolingCount":3,"poolingPeak":3,"poolingPeakTime":"2019-09-22 21:21:54","connectCount":2,"closeCount":2,"connectionHoldTimeHistogram":[0,0,2]}
2019-09-22 21:31:54.505 INFO 16972 --- [-Log-1465691120] c.a.d.p.DruidDataSourceStatLoggerImpl : {"url":"jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false","dbType":"mysql","name":"DataSource-1465691120","activeCount":0,"poolingCount":3,"connectCount":0,"closeCount":0}
2019-09-22 21:36:54.505 INFO 16972 --- [-Log-1465691120] c.a.d.p.DruidDataSourceStatLoggerImpl : {"url":"jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false","dbType":"mysql","name":"DataSource-1465691120","activeCount":0,"poolingCount":3,"connectCount":0,"closeCount":0}
复制代码
从时间上能够看出,确实是每5分钟会输出一次。
打开浏览器访问:http://localhost:8080/druid/ ,查看 Druid 监控页面,结果如图:
咱们能够进行一些接口测试,在查看监控页面,能够看到全部的 SQL 都正常记录,如图:
同时,咱们看一下后台的日志打印,是否正常打出记录的日志,截取打印部分,以下:
2019-09-22 21:51:54.506 INFO 16972 --- [-Log-1465691120] c.a.d.p.DruidDataSourceStatLoggerImpl : {"url":"jdbc:mysql://192.168.0.128:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false","dbType":"mysql","name":"DataSource-1465691120","activeCount":0,"activePeak":1,"activePeakTime":"2019-09-22 21:47:28","poolingCount":3,"poolingPeak":3,"poolingPeakTime":"2019-09-22 21:47:28","connectCount":4,"closeCount":4,"executeCount":4,"commitCount":4,"pstmtCacheHitCount":2,"pstmtCacheMissCount":2,"startTransactionCount":4,"transactionHistogram":[0,1,2,1],"connectionHoldTimeHistogram":[0,1,0,3],"sqlList":[{"sql":"insert into user (age, nick_name, id) values (?, ?, ?)","executeCount":2,"executeMillisMax":1,"executeMillisTotal":2,"executeHistogram":[1,1],"executeAndResultHoldHistogram":[1,1],"concurrentMax":1,"updateCount":2,"updateCountMax":1,"updateHistogram":[0,2],"inTransactionCount":2},{"sql":"select usermodel0_.id as id1_0_, usermodel0_.age as age2_0_, usermodel0_.nick_name as nick_nam3_0_ from user usermodel0_ order by usermodel0_.id desc","executeCount":2,"executeMillisMax":3,"executeMillisTotal":4,"executeHistogram":[0,2],"executeAndResultHoldHistogram":[2],"concurrentMax":1,"fetchRowCount":4,"fetchRowCountMax":2,"fetchRowHistogram":[0,2],"inTransactionCount":2}]}
复制代码
能够看到,日志中打印了咱们执行的 SQL 相关的信息,和咱们在监控页面看到的信息彻底一致。
至此,测试成功,篇幅缘由,一些测试过程未列出,各位感兴趣的读者朋友能够本身动手尝试一下。