SpringBoot整合Druid实现数据监控
druid是一个数据库连接池,是alibaba的一个开源项目,支持所有JDBC兼容数据库。使用Druid连接池在一定程度上可以提高数据访问效率。
druid提供了数据监控的功能,可以对SQL监控,对所有的请求的SQL进行分析统计,可以对SQL进行安全防御并克查看相关数据。
引入依赖
引入druid、log4j依赖
implementation group: 'com.alibaba', name: 'druid-spring-boot-starter', version: '1.1.23'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2', version: '2.4.3'
配置
application.yml
首先配置druid数据源及JDBC配置,SpringBoot默认使用hikari,如果不配置会使用默认的hikari
JDBC配置
spring:
datasource:
########## JDBC 基本配置 ##########
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver # mysql8 的连接驱动
url: jdbc:mysql://localhost:3306/fxfdb_11328_durild?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
# platform: mysql # 数据库类型
type: com.alibaba.druid.pool.DruidDataSource # 指定数据源类型
连接池配置
########## 连接池 配置 ##########
druid:
# 配置初始化大小、最小、最大
initial-size: 5
minIdle: 10
max-active: 20
# 配置获取连接等待超时的时间(单位:毫秒)
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 2000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 600000
# max-evictable-idle-time-millis: 900000
# 用来测试连接是否可用的SQL语句,默认值每种数据库都不相同,这是mysql
validationQuery: select 1
# 应用向连接池申请连接,并且testOnBorrow为false时,连接池将会判断连接是否处于空闲状态,如果是,则验证这条连接是否可用
testWhileIdle: true
# 如果为true,默认是false,应用向连接池申请连接时,连接池会判断这条连接是否是可用的
testOnBorrow: false
# 如果为true(默认false),当应用使用完连接,连接池回收连接的时候会判断该连接是否还可用
testOnReturn: false
# 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle
poolPreparedStatements: true
# 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true,
# 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,
# 可以把这个数值配置大一些,比如说100
maxOpenPreparedStatements: 20
# 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作
keepAlive: true
# Spring 监控,利用aop 对指定接口的执行时间,jdbc数进行记录
aop-patterns: "com.springboot.template.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
# 日志监控,使用slf4j 进行日志输出
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关联监控的数据 sql监控 统计 web 应用请求中所有的数据库信息 ##########
监控配置
配置 Druid web 监控 filter过滤器 WebStatFilter配置
web-stat-filter:
enabled: true # 启动 StatFilter
url-pattern: /* # 过滤所有url
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" # 排除一些不必要的url
session-stat-enable: true # 开启session统计功能
session-stat-max-count: 1000 # session的最大个数,默认100
配置 Druid 后台管理 Servlet StatViewServlet配置,用来配置监控页面
########## 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ##########
stat-view-servlet:
enabled: true # 启用StatViewServlet
url-pattern: /druid/* # 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
reset-enable: false # 不允许清空统计数据,重新计算
login-username: root # 配置监控页面访问密码
login-password: 123
allow: 127.0.0.1 # 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
deny: # 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝
查看监控页面
启动项目后访问/druid/login.html登录页面,账号密码在yml中配置
注意:如果配置了拦截器需要放行Druid的静态资源资源
登陆页面
首页
获取 Druid 的监控数据
我们可以通过接口来获取Druid的监控数据,可以根据需要自由地对监控数据、暴露方式进行扩展。
@RestController
public class DruidStatController {
@GetMapping("/druid/stat")
public Object druidStat(){
// DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据,除此之外 DruidStatManagerFacade 还提供了一些其他方法,你可以按需选择使用。
return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
}
}
去Alibaba广告
登录进入druid监控首页后发现又阿里巴巴的广告,可以通过滤器过滤,注册一个过滤器,过滤common.js的请求,使用正则表达式替换相关的广告内容
@Configuration
@ConditionalOnWebApplication
@AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled",
havingValue = "true", matchIfMissing = true)
public class DruidConfig {
/**
* 方法名: removeDruidAdFilterRegistrationBean
* 方法描述 除去页面底部的广告
* @param properties com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties
* @return org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
//创建filter进行过滤
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
// 重置缓冲区,响应头不会被重置
response.resetBuffer();
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
text = text.replaceAll("powered.*?shrek.wang</a>", "");
response.getWriter().write(text);
}
@Override
public void destroy() {}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}
踩坑
集成druid启动日志报错
discard long time none received connection. , jdbcUrl : jdbc:mysql://192.168.1.10:3306/fztestdb...
原因
百度查了一下这是因为新版本的druid的问题,如果一个连接空闲超过60s,druid会将之丢弃(holder)并且打印一条warn日志。
查看druid源码
//如果一个连接空闲超过60s,druid会将之丢弃(holder)并且打印一条warn日志。
if (valid && isMySql) { // unexcepted branch
long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
if (lastPacketReceivedTimeMs > 0) {
long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
if (lastPacketReceivedTimeMs > 0 //
&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
discardConnection(holder);
String errorMsg = "discard long time none received connection. "
+ ", jdbcUrl : " + jdbcUrl
+ ", version : " + VERSION.getVersionNumber()
+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
LOG.warn(errorMsg);
return false;
}
}
}
解决
给druid加一条配置: druid.mysql.usePingMethod=false
DruidConfig类中添加
/**
* 关闭druid检查连接
* 解决druid 日志报错:discard long time none received connection:xxx
*/
@PostConstruct
public void setProperties(){
System.setProperty("druid.mysql.usePingMethod","false");
}