SpringBoot3.x使用Druid
- BiliBili影片
- 官網:druid/druid-spring-boot-starter at master · alibaba/druid
- 參考文章
- Spring Boot 3 整合 Druid 連線池詳解
起步
安裝
- 最新版本查詢:https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-3-starter
- 注意:Druid配置變更後需要重啟才能生效
<properties>
<druid.version>1.2.21</druid.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- SpringBoot3.x使用這個庫,才能夠開啟Druid監控網頁 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
</dependencies>
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource # Druid連線池!
# 以下注釋內容與原資料庫連線相同
url: jdbc:mysql://***
username: ***
password: ***
driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.druid.socket-timeout= 6000 # 請求超時時間(單位:毫秒)
spring.datasource.druid.initial-size=
spring.datasource.druid.max-active=
spring.datasource.druid.min-idle=
spring.datasource.druid.max-wait=
spring.datasource.druid.pool-prepared-statements=
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=
spring.datasource.druid.max-open-prepared-statements= #和上面的等價
spring.datasource.druid.validation-query=
spring.datasource.druid.validation-query-timeout=
spring.datasource.druid.test-on-borrow=
spring.datasource.druid.test-on-return=
spring.datasource.druid.test-while-idle=
spring.datasource.druid.time-between-eviction-runs-millis=
spring.datasource.druid.min-evictable-idle-time-millis=
spring.datasource.druid.max-evictable-idle-time-millis=
spring.datasource.druid.filters= #配置多個英文逗號分隔
....//more
基礎連線資訊
- 非必須,無聊看著玩的
- 建立任意控制器和任意路徑,返回Druid的連線資訊
@RestController
@RequestMapping("/index")
public class IndexController {
@GetMapping("/druid-status")
public Object druidStat(){
return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
}
}
[
{
"Identity": 1583082378,
"Name": "DataSource-1583082378",
"DbType": "h2",
"DriverClassName": "org.h2.Driver",
"URL": "jdbc:h2:file:./demo-db",
"UserName": "sa",
"FilterClassNames": [
"com.alibaba.druid.filter.stat.StatFilter"
],
"WaitThreadCount": 0,
"NotEmptyWaitCount": 0,
"NotEmptyWaitMillis": 0,
"PoolingCount": 2,
"PoolingPeak": 2,
"PoolingPeakTime": 1533782955104,
"ActiveCount": 0,
"ActivePeak": 1,
"ActivePeakTime": 1533782955178,
"InitialSize": 2,
"MinIdle": 2,
"MaxActive": 30,
"QueryTimeout": 0,
"TransactionQueryTimeout": 0,
"LoginTimeout": 0,
"ValidConnectionCheckerClassName": null,
"ExceptionSorterClassName": null,
"TestOnBorrow": true,
"TestOnReturn": true,
"TestWhileIdle": true,
"DefaultAutoCommit": true,
"DefaultReadOnly": null,
"DefaultTransactionIsolation": null,
"LogicConnectCount": 103,
"LogicCloseCount": 103,
"LogicConnectErrorCount": 0,
"PhysicalConnectCount": 2,
"PhysicalCloseCount": 0,
"PhysicalConnectErrorCount": 0,
"ExecuteCount": 102,
"ErrorCount": 0,
"CommitCount": 100,
"RollbackCount": 0,
"PSCacheAccessCount": 100,
"PSCacheHitCount": 99,
"PSCacheMissCount": 1,
"StartTransactionCount": 100,
"TransactionHistogram": [
55,
44,
1,
0,
0,
0,
0
],
"ConnectionHoldTimeHistogram": [
53,
47,
3,
0,
0,
0,
0,
0
],
"RemoveAbandoned": false,
"ClobOpenCount": 0,
"BlobOpenCount": 0,
"KeepAliveCheckCount": 0,
"KeepAlive": false,
"FailFast": false,
"MaxWait": 1234,
"MaxWaitThreadCount": -1,
"PoolPreparedStatements": true,
"MaxPoolPreparedStatementPerConnectionSize": 5,
"MinEvictableIdleTimeMillis": 30001,
"MaxEvictableIdleTimeMillis": 25200000,
"LogDifferentThread": true,
"RecycleErrorCount": 0,
"PreparedStatementOpenCount": 1,
"PreparedStatementClosedCount": 0,
"UseUnfairLock": true,
"InitGlobalVariants": false,
"InitVariants": false
}
]
Druid監控皮膚
啟用皮膚
- Druid提供了大量監控資料,監控資料只儲存在記憶體中,重啟後會丟失,且無法集中檢視
- 訪問地址(在url-pattern配置):http://localhost:8080/druid
- 可訪問頁面
- IP轉發問題
- Nginx轉發
- 使用Nginx代理轉發,原訪問IP會被替換成Nginx的IP,所以allow需要設定成Nginx伺服器的IP
- 但是所有訪問IP都被替換成Nginx的IP,所有請求都會放行,相當於allow失去了作用,記得使用賬號
- 最佳實踐:外網Nginx直接遮蔽druid介面,開發機直接內網訪問原始伺服器地址,allow設定成開發機IP
- k8s叢集代理
- k8s叢集Ingress的IP是動態的,無法設定成準確IP,可以設定為"192.168.0.0/16,10.0.0.0/8"放行所有IP(同樣有安全問題,記得增加賬號密碼)
- 最佳實踐:不要暴露druid介面,開發機使用"kubectl port-forward"或者"ktctl forward"命令將服務轉到本地,再透過localhost訪問
- 其他
- Nginx中設定"proxy_set_header Host $host;"的解決方案,在目前的最新版"1.2.21"中是無效的
spring:
datasource:
druid:
stat-view-servlet:
enabled: true # 啟用Druid監控皮膚
url-pattern: /druid/* # 皮膚路徑(預設"/druid/*")(Druid已做了處理,不會被自定義過濾器攔截)
reset-enable: false # 皮膚上的重置按鈕(禁用後皮膚上重置按鈕依然會顯示和互動,但是不起作用)
login-username: admin # 賬號(預設不需要登入,設定了才需要)
login-password: 123456 # 密碼
allow: 127.0.0.1 # 白名單,多個使用逗號隔開(不在白名單中不能訪問,預設開啟localhost/127.0.0.1)
deny: 127.0.0.1 # 黑名單,多個使用逗號隔開
# 使用Nginx轉發導致訪問IP丟失,allow、deny均失效,可將allow設定為 192.168.0.0/16,10.0.0.0/8 允許所有請求,注意使用賬號保護
SQL監控
spring:
datasource:
druid:
filter:
stat:
enabled: true # 啟動SQL語句監控,同時會影響其他皮膚中的SQL展示(如:影響"URI監控"中的jdbc資料)
db-type: mysql # 資料庫型別(實測沒啥用,會自動根據上面的連線自動識別)
log-slow-sql: true # 記錄慢日誌("SQL監控"皮膚中數值會標記為紅色)
slow-sql-millis: 3000 # 慢日誌判定標準(單位:毫秒)
slow-sql-log-level: ERROR # 自定義日誌級別,預設日誌級別"ERROR"
SQL防火牆
- 檢查SQL語句,防止惡意操作
- 同時記錄每一次Select/Insert/Update/...等操作
spring:
datasource:
druid:
filter:
wall:
enabled: true # 啟用防火牆
db-type: h2 # 資料庫型別(實測沒啥用,會自動根據上面的連線自動識別)
config:
delete-allow: false # 是否允許DELETE操作(報錯異常:java.sql.SQLException:sql injection violation, dbType mysql, druid-version 1.2.21, delete not allow : DELETE FROM ...)
drop-table-allow: false # 是否允許DROP TABLE操作
Web應用/URI監控/Session監控
- 影響到"Web應用/URI監控/Session監控"皮膚
spring:
datasource:
druid:
web-stat-filter:
enabled: true # 啟用"Web應用/URI監控/Session監控"皮膚
url-pattern: /* # 監控這裡指定的路徑
exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,*.html,*.json # 過濾路徑
# "Session監控"皮膚設定(RestFul純後端沒有session)
session-stat-enable: true
session-stat-max-count: 1000
principal-session-name: xxc
principal-cookie-name: xxc
profile-enable: true
Spring監控
spring:
datasource:
druid:
aop-patterns: x.y.z.controller.* # Spring監控AOP切入點,多個用逗號分隔
其他
- 官網:druid/druid-spring-boot-starter at master · alibaba/druid
- 所有過濾器(上面配置值包含一部分)
- StatFilter
- WallFilter
- ConfigFilter
- EncodingConvertFilter
- Slf4jLogFilter
- Log4jFilter
- Log4j2Filter
- CommonsLogFilter
問題
超時
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure...
...
The last packet successfully received from the server was 11,034 milliseconds ago. The last packet sent successfully to the server was 20,386 milliseconds ago...
LastErrorMessage:
Communications link failure The last packet successfully received from the server was 11,034 milliseconds ago. The last packet sent successfully to the server was 20,386 milliseconds ago.
LastErrorClass:
com.mysql.cj.jdbc.exceptions.CommunicationsException
LastErrorStackTrace:
com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:912)
com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:354)
com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3462)
com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:434)
com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3460)
com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:158)
com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:483)
org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
jdk.internal.reflect.GeneratedMethodAccessor43.invoke(Unknown Source)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:568)
org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:64)
jdk.proxy2/jdk.proxy2.$Proxy170.query(Unknown Source)
org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:81)
org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
jdk.proxy2/jdk.proxy2.$Proxy169.query(Unknown Source)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
...
spring:
datasource:
druid:
socket-timeout: 60000 # 連線超時時間,60000毫秒(1分鐘)