1. 基本概念
Druid 是 Java 語言中最好的數據庫連接池。
雖然 HikariCP 的速度稍快,但是,Druid 能夠提供強大的監控和擴展功能。
而 Druid 已經在阿里巴巴部署了超過600個引用,經過好幾年生產環境大規模部署的嚴苛考驗!
- stat: Druid 內置提供一個 StatFilter,用於統計監控信息。
- wall: Druid 防御 SQL 注入攻擊的 WallFilter 就是通過 Druid 的 SQL Parser 分析。Druid 提供的 SQL Parser 可以在 JDBC 層攔截 SQL 做相應處理,比如說分庫分表、審計等。
- log4j2: 這個就是日志記錄的功能,可以把 sql 語句打印到 Log4j2 供排查問題。
2. 添加依賴
項目 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 http://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.5.9</version>
<relativePath />
</parent>
<groupId>com.ninaco.xms</groupId>
<artifactId>xms</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. 配置相關屬性
application.yml
# 配置數據源(Druid)
spring:
datasource:
# JDBC 基本配置
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver # mysql8 驅動
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource # 指定數據源
# 連接池配置
druid:
# 配置初始化大小、最小、最大
initial-size: 5
min-idle: 10
max-active: 20
# 配置獲取連接等待超時的時間(單位:毫秒)
max-wait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空箱連接,單位是毫秒
time-between-eviction-runs-millis: 200
# 配置一個連接在池中最小生存的世界,單位是毫秒
min-evictable-idle-time-millis: 600000
max-evictable-idle-time-millis: 900000
# 用來測試連接是否可用的SQL語句,默認值每種數據庫都不相同,這是 mysql
validation-query: select 1
# 應用向連接池申請連接,並且 testOnBorrow 為false時,連接池將會判斷連接是否處於空閑狀態,如果是,則驗證這條連接是否可用
test-while-idle: true
# 如果為 true,默認是false,應用向連接池申請連接時,連接池會判斷這條連接是否是可用的
test-on-borrow: false
# 如果為 true(默認 false),當應用使用完連接,連接池回收連接的時候會判斷改連接是否還可用
test-on-return: false
# 是否緩存 preparedStatement,也就是 PSCache。PSCache對支持游標的數據庫性能提升巨大,比如 oracle
pool-prepared-statements: true
# 要啟用 PSCache,必須配置大於0,當大於0時,poolPreparedStatements 自動觸發修改為 true,
# 在 Druid 中,不會存在 Oracle下 PSCache 占用內存過多的問題,可以吧這個數值配置大一些,比如說 100
max-open-prepared-statements: 20
# 連接池中的 minIdle 數量以內的連接,空閑時間超過 minEVictableIdleTimeMillis,則會執行 keepAlive 操作
keep-alive: true
# Spring 監控,利用 aop 對指定接口的執行時間,jdbc數進行記錄
aop-patterns: "com.ninaco.xms.dao.*"
## 啟用內置過濾器(第一個 stat 必須,否則監控不到 SQL)
filters: stat,wall,log4j2
# 自己配置監控統計攔截的 filter
filter:
# 開啟 druiddatasource 的狀態監控
stat:
enabled: true
db-type: mysql
# 開啟慢 sql 監控,超過 2s 就認為是 慢 sql,記錄到日志中
log-slow-sql: true
slow-sql-millis: 2000
# 日志監控,使用 slf4j2 進行日志輸出
slf4j:
enabled: true
statement-log-error-enabled: true
statement-create-after-log-enabled: false
statement-close-after-log-enabled: false
result-set-open-after-log-enabled: false
result-set-close-after-log-enabled: false
## 配置 WebStatFilter,用於采集 web 關聯監控的數據
web-stat-filter:
# 啟動 StatFilter
enabled: true
# 過濾所有 url
url-pattern: /*
# 排除一些不必要的url
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# 開啟 session 統計功能
session-stat-enable: true
# session 的最大個數,默認100
session-stat-max-count: 1000
## 配置 StatViewServlet(監控頁面),用於展示 Druid 的統計信息
stat-view-servlet:
# 啟用 StatViewServlet
enabled: true
# 訪問內置監控頁面的路徑,內置監控頁面的首頁是 /druid/index.html
url-pattern: /druid/*
# 不允許清空統計數據,重新計算
reset-enable: false
# 配置監控頁面訪問用戶和密碼
login-username: root
login-password: 123
# 允許訪問的地址,如果 allow 沒有配置或者為空,則允許所有訪問
allow: 127.0.0.1
# 拒絕訪問的地址,deny優先於 allow,如果在 deny 列表中,就算在 allow 列表中,也會被拒絕
deny:
上述配置文件的參數可以在
com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties
和org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
中找到;
4. 監控界面
- 啟動項目后,訪問
/druid/login.html
來到登錄頁面,輸入用戶名密碼登錄
- 數據源頁面是當前 DataSource 配置的基本信息,上述配置的 Filter 可以在里面找到,如果沒有配置 Filter(一些信息會無法統計,例如“SQL 監控”,會無法獲取 JDBC 相關的 SQL 執行信息)
- SQL 監控頁面,統計了所有 SQL 語句的執行情況
- URL 監控頁面,統計了所有 Controller 接口的訪問以及執行情況
- Spring 監控頁面,利用 aop 對指定接口的執行時間,jdbc 數進行記錄
- SQL 防火牆頁面,druid 提供了黑白名單的訪問,可以清楚的看到 sql 防護情況
- Session 監控頁面,可以看到當前的 session 狀況,創建時間、最后的活躍時間、請求次數、請求時間等詳細參數。
- JSONAPI 頁面,通過 api 的形式訪問 Druid 的監控接口,api放回 json 形式數據
5. spring 監控
訪問之后 spring 講課默認是沒有數據的;這需要導入 SpringBoot 的 AOP 的 Starter
<!--SpringBoot 的aop 模塊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
之后在 application.yml
配置 AOP 切入點
:
spring:
datasource:
druid:
# Spring 監控,利用 aop 對指定接口的執行時間,jdbc數進行記錄
aop-patterns: "com.ninaco.xms.dao.*"
Spring 監控 AOP 切入點,如
com.ninaco.xms.dao.*
, 配置多個英文逗號分隔
6. 獲取 Druid 的監控數據
Druid 的監控數據可以在 開啟 StatFilter 后,通過 DruidStatManagerFacade 進行獲取;
DruidStatManagerFacade#getDataSourceStatDataList 該方法可以獲取所有數據源的監控數據,除此之外 DruidStatManagerFacade 還提供了一些其他方法,可以按需選擇使用。
@RestController
@RequestMapping("/druid")
public class DruidStatController {
@GetMapping("/stat")
public Object druidStat() {
// 獲取數據源的監控數據
return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
}
}