SpringBoot集成DruidDataSource实现监控 SQL 性能

文章介绍了如何在SpringBoot中配置和使用Druid数据源,包括设置基本属性、自定义配置、内置过滤器和监控功能。Druid提供了强大的监控和扩展能力,如SQL监控、Web应用监控、防火墙等功能,通过配置可以实现详细的数据源监控和日志输出。
摘要由CSDN通过智能技术生成

在这里插入图片描述

一、快速入门

1.1 基本概念

  我们都使用过连接池,比如C3P0、DBCP、hikari、Druid,虽然 HikariCP 的速度稍快,但 Druid 能够提供强大的监控和扩展功能。Druid DataSource 是阿里巴巴开发的号称为监控而生的数据库连接池,它不仅可以获取数据库连接,还把这些数据库连接管理了起来,也就是所谓的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,可以说是 Java 语言中最好的数据库连接池。
  Spring Boot 2.0 以上默认使用 Hikari 数据源,而Druid已经在阿里巴巴部署了超过600个应用,经过好几年生产环境大规模部署的严苛考验!Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

1.2 如何使用 DruidDataSource

首先看一下怎么使用 DruidDataSource。

// 配置一个 DruidDataSource 实例 bean 交由 Spring 容器管理
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/transaction?useSSL=false");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        //连接池最小空闲的连接数
        dataSource.setMinIdle(5);
        //连接池最大活跃的连接数
        dataSource.setMaxActive(20);
        //初始化连接池时创建的连接数
        dataSource.setInitialSize(10);
        return dataSource;
    }
}
 
//测试类
public class DataSourceMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DataSourceConfig.class);
        //获取 dataSource 实例
        DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
        try {
            //通过数据源获取数据库连接
            Connection connection = dataSource.getConnection();
            Statement statement = connection.createStatement();
            statement.execute("update AccountInfo set balance = balance + 1 where id = 1");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

  从上可以看出,通过数据源访问跟 jdbc 方式相比,省略了实例化数据库连接驱动 Driver 驱动这一步,此外调用 getConnection() 获取连接的时候,并没有传用户名和密码,说明 dataSource 把这些配置信息都管理起来了。总结来说,DruidDataSource 管理了数据库连接的一些配置信息,还帮助我们创建了连接驱动 Driver,剩下的逻辑就跟 jdbc 一毛一样了。

1.3 通用配置

DruidDataSource大部分属性都是参考DBCP的,个别配置的语意有所区别。如果你原来就是使用DBCP,迁移是十分方便的。

com.alibaba.druid.pool.DruidDataSource

配置缺省值说明
name如果存在多个数据源,监控的时候可以通过名字来区分开来
url连接数据库的url,不同数据库不一样
username连接数据库的用户名
password连接数据库的密码。
如果不希望密码直接写在配置文件中,可以使用ConfigFilter
driverClassName这一项可配可不配,根据url自动识别
maxActive8最大连接池数量
initialSize0初始化时建立连接的个数
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。
filters监控统计拦截的filters
timeBetweenEvictionRunsMillis间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis一个连接在池中最大生存的时间,单位是毫秒
validationQuery用来检测连接是否有效的sql。
如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
validationQueryTimeout检测连接是否有效的超时时间,单位:秒
testWhileIdle如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,配置后会降低性能
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,配置后会降低性能
keepAlivefalse连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
poolPreparedStatementsfalse是否缓存preparedStatement
maxOpenPreparedStatements-1是否要启用PSCache。当大于0时,启用。
asyncInitfalse
connectionInitSqls物理连接初始化的时候执行的sql

1.4 内置Filter的别名

在META-INF/druid-filter.properties文件中配置Filter的别名。

Filter类名别名
defaultcom.alibaba.druid.filter.stat.StatFilter
statcom.alibaba.druid.filter.stat.StatFilter
mergeStatcom.alibaba.druid.filter.stat.MergeStatFilter
encodingcom.alibaba.druid.filter.encoding.EncodingConvertFilter
log4jcom.alibaba.druid.filter.logging.Log4jFilter
log4j2com.alibaba.druid.filter.logging.Log4j2Filter
slf4jcom.alibaba.druid.filter.logging.Slf4jLogFilter
commonloggingcom.alibaba.druid.filter.logging.CommonsLogFilter
wallcom.alibaba.druid.wall.WallFilter

二、相关配置

2.1 添加依赖

首先我们需要在应用的 pom.xml 文件中添加上 Druid 数据源依赖,可以从 Maven 仓库官网 中获取。

<!-- Druid 数据源依赖,集成了 Spring boot ,方便配置 druid 属性 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.10</version>
</dependency>

<!-- Mysql 数据库驱动,spring boot 2.1.3 时,mysql 驱动版本为 8.0.15 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

<!--引用 log4j2 spring boot 启动器,内部依赖了 slf4j、log4j;排除项目中的 logback-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<!-- mybatis,引入了 SpringBoot的 JDBC 模块,所以,默认是使用 hikari 作为数据源 -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.0.1</version>
	<exclusions>
		<!-- 排除默认的 HikariCP 数据源 -->
		<exclusion>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
		</exclusion>
	</exclusions>
</dependency>

2.2 配置数据源相关属性

2.2.1 配置Druid 数据源

  从 Spring Boot 2.0 开始,默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但其中有一个十分重要的条件,如下图。

在这里插入图片描述

   @ConditionalOnMissingBean(DataSource.class) 的含义是:当容器中没有 DataSource 时,Spring Boot 才会使用 HikariCP 作为其默认数据源。 也就是说,若向容器中添加 Druid 数据源类的对象时,Spring Boot 就会使用 Druid 作为其数据源,而不再使用 HikariCP。在配置文件 application.yml 中添加以下数据源配置,它们会与与 Druid 数据源中的属性进行绑定,如下所示:

spring:
  datasource:
    username: root
    password: root
    # driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=true
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource

com.alibaba.druid.pool.DruidDataSource 基本配置参数,如下所示:

配置说明
jdbcUrl连接数据库的url,多数据源时使用,不同数据库不一样
url连接数据库的url,单数据源时使用,不同数据库不一样
username连接数据库的用户名
password连接数据库的密码
driverClassName这一项可配可不配,不配置时druid会根据url自动识别dbType,然后选择相应的driverClassName
connectionInitSqls连接初始化的时候执行的sql
exceptionSorter当数据库抛出一些不可恢复的异常时,抛弃连接

注:配置类创建 Druid 数据源对象时,应该尽量避免将数据源信息硬编码到代码中,而应该通过@ConfigurationProperties(“spring.datasource”) 注解,将 Druid 数据源对象的属性与配置文件中的以“spring.datasource”开头的配置进行绑定。

至此,我们就已经将数据源从 HikariCP 切换到了 Druid 了。

2.2.2 Druid 数据源测试

当数据源配置之后,我们需要来验证数据源类型是否为 Druid 以及是否能正常获取数据库连接、访问数据库,代码如下。

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.*;

/**
 * 把今天最好的表现当作明天最新的起点..~
 * <p>
 * Today the best performance as tomorrow the newest starter!
 *
 * @类描述: 数据源测试,测试 spring.datasource.xx 的 druid 属性配置是否正常,数据库是否能连接上等等
 * @author: <a href="mailto:duleilewuhen@sina.com">独泪了无痕</a>
 * @创建时间: 2023-02-28 00:48
 * @版本: V 1.0.1
 * @since: JDK 1.8
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class DataSourceTest {
    /**
     * Spring Boot 默认已经配置好了数据源,可以直接 DI 注入然后使用即可
     */
    @Resource
    private DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        System.out.println("数据源>>>>>>" + dataSource.getClass());
        Connection connection = dataSource.getConnection();
        System.out.println("连接>>>>>>>>>" + connection);
        System.out.println("连接地址>>>>>" + connection.getMetaData().getURL());

        if (dataSource instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
            System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());
            System.out.println("version=" + druidDataSource.getVersion());
            System.out.println("name=" + druidDataSource.getName());
        }
        connection.close();
    }
}

2.3 自定义 Druid 数据源

2.3.1 Druid 数据源参数配置

  如同以前 c3p0、dbcp 数据源可以设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等一样,Druid 数据源同理可以进行设置。Druid Spring Boot Starter 配置属性的名称完全遵照 Druid,可以通过 Spring Boot 配置文件来配置Druid数据库连接池和监控,如果没有配置则使用默认值,如下在 application.yml 配置相关属性:

spring:
  datasource:
    # druid 数据源专有配置,对应的是 com.alibaba.druid.pool.DruidDataSource 中的属性
    druid:
      # 数据源名称:当存在多个数据源时,设置名字可以很方便的来进行区分,默认自动生成名称,格式是:"DataSource-" + System.identityHashCode(this)
      name: druid-db1
      # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时,默认0
      initialSize: 10
      # 最大连接池数量,默认8
      maxActive: 200
      # 最小连接池数量
      minIdle: 10
      # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁
      maxWait: 60000
      # 1)Destroy线程会检测连接的间隔时间 2)testWhileIdle的判断依据(详细看testWhileIdle属性的说明)
      timeBetweenEvictionRunsMillis: 60000
      # 设置连接最小可收回空闲时间(毫秒),默认为 1000L * 60L * 30L
      minEvictableIdleTimeMillis: 300000
      # 设置连接最大可收回空闲时间(毫秒),默认为 1000L * 60L * 60L * 7
      maxEvictableIdleTimeMillis: 900000
      # 用来检测连接是否有效的sql,求是一个查询语句。默认为null,此时testOnBorrow、testOnReturn、testWhileIdle都不会其作用
      validationQuery: SELECT 1 FROM DUAL
      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认为true
      testWhileIdle: true
      # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,默认false
      testOnBorrow: false
      # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,默认false
      testOnReturn: false
      # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
      poolPreparedStatements: true
      # 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true,
      # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      maxOpenPreparedStatements: 20
      # 连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis,则会执行 keepAlive 操作
      keepAlive: true
      # 每个连接大小的最大池准备语句数
      maxPoolPreparedStatementPerConnectionSize: 20
      # 是否使用全局数据源统计,默认false
      useGlobalDataSourceStat: true
      # 连接属性
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      # 配置监控统计的内置过滤器:
      # stat-监控统计(必须配置,否则监控不到sql)
      # wall-防御sql注入
      # log4j2-日志记录框架(值与应用中的日志框架保持一致,如 log4j、log4j、logback、slf4j)
      filters: stat,wall,log4j2

现在可以重新测试 Druid 数据源,查看配置文件中的参数是否已经生效,也可以直接 Debug 查看 DruidDataSource 的属性。

2.3.2 配置 Druid 数据源监控

  这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等。可以通过 spring.datasource.druid.filters=stat,wall,log4j ...的方式来启用相应的内置Filter,不过这些Filter都是默认配置。如果默认配置不能满足需求,可以放弃这种方式,通过配置文件来配置Filter,下面是例子。

########## JDBC基本配置 ##########

spring.datasource.username=developer
# spring.datasource.druid.username
spring.datasource.password=developer
# mysql的连接驱动
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# spring.datasource.druid.url
spring.datasource.url=jdbc:mysql://localhost:3306/opensource?useUnicode=true&characterEncoding=UTF-8&useSSL=true
# 数据库类型
spring.datasource.platform=mysql
# 指定数据源类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

########## druid数据源专有配置,对应的是com.alibaba.druid.pool.DruidDataSource中的属性
# 配置监控统计的内置过滤器:
spring.datasource.druid.filters=stat,wall,slf4j

##########自定义过滤器配置:stat、slf4j、log4j、log4j2、commons-log、wal
# 内置Filter都是默认配置,无法满足需求时,则可以自定义Filter,自定义的过滤器默认都是没有开启的

# 开启DruidDataSource的状态监控,必须配置,否则监控不到sql
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.filter.stat.db-type=mysql
# 开启慢sql监控,超过2s就认为是慢sql,记录到日志中
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000

########## 日志监控,使用slf4j进行日志输出

spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-log-error-enabled=true
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false

# 防火墙过滤器,防御sql注入
spring.datasource.druid.filter.wall.enabled=true
# 不允许删除数据
spring.datasource.druid.filter.wall.config.delete-allow=false

##########配置WebStatFilter,用于采集web关联监控的数据##########

# 是否启用StatFilter默认值false
spring.datasource.druid.web-stat-filter.enabled=true
# 过滤所有url
spring.datasource.druid.web-stat-filter.url-pattern=/*
# 排除一些不必要的url
spring.datasource.druid.web-stat-filter.exclusions="*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# 开启session统计功能
spring.datasource.druid.web-stat-filter.session-stat-enable=true
# session的最大个数,默认100
spring.datasource.druid.web-stat-filter.session-stat-max-count=1000
# 使得druid能够知道当前的session的用户是谁
# spring.datasource.druid.web-stat-filter.principal-session-name=
# 如果你的user信息保存在cookie中,你可以配置principalCookieName,使得druid知道当前的user是谁
# spring.datasource.druid.web-stat-filter.principal-cookie-name=
# 配置profileEnable能够监控单个url调用的sql列表
# spring.datasource.druid.web-stat-filter.profile-enable=

########## 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ##########

# 是否启用StatViewServlet(监控页面)默认值为false
spring.datasource.druid.stat-view-servlet.enabled=true
# 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
# 不允许清空统计数据,重新计算
spring.datasource.druid.stat-view-servlet.reset-enable=false
# 配置监控页面访问用户名
spring.datasource.druid.stat-view-servlet.loginUsername=root
# 配置监控页面访问密码
spring.datasource.druid.stat-view-servlet.loginPassword=123
# 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
spring.datasource.druid.stat-view-servlet.allow=127.0.0.1
# 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝
spring.datasource.druid.stat-view-servlet.deny=

不想使用内置的 Filters,要想使自定义 Filter 配置生效需要将对应 Filter 的 enabled 设置为 true ,Druid Spring Boot Starter 默认禁用 StatFilter,可以将其 enabled 设置为 true 来启用它。

2.3.3 Druid 内置 Filter 配置

  Druid Spring Boot Starter 对以下 Druid 内置 Filter,都提供了默认配置StatFilter、WallFilter、ConfigFilter、EncodingConvertFilter、Slf4jLogFilter、Log4jFilter、Log4j2Filter、CommonsLogFilter。我们可以通过 spring.datasource.druid.filters=stat,wall … 的方式来启用相应的内置 Filter,不过这些 Filter 使用的都是默认配置。如果默认配置不能满足我们的需求,我们还可以在配置文件使用 spring.datasource.druid.filter.* 对这些 Filter 进行配置,示例代码如下。

#  ####################################################### Druid 监控配置信息  ##########################################
spring:
  datasource:
    druid:
     # 对配置已开启的 filters 即 stat(sql 监控)  wall(防火墙)
      filter:
        #配置StatFilter (SQL监控配置)
        stat:
          enabled: true                                                               #开启 SQL 监控
          slow-sql-millis: 1000                                                       #慢查询
          log-slow-sql: true                                                          #记录慢查询 SQL
 
        #配置WallFilter (防火墙配置)
        wall:
          enabled: true                                                               #开启防火墙
          config:
            update-allow: true                                                        #允许更新操作
            drop-table-allow: false                                                   #禁止删表操作
            insert-allow:  true                                                       #允许插入操作
            delete-allow: true                                                        #删除数据操作

在配置 Druid 内置 Filter 时,需要先将对应 Filter 的 enabled 设置为 true,否则内置 Filter 的配置不会生效。

2.4 Druid 内置监控页面

  Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装路由器时,人家也提供了一个默认的 web 页面。首先需要设置 Druid 的后台管理页面的属性,比如登录账号、密码等。

2.4.1 开启 Druid 内置监控

  Druid 内置提供了一个名为 StatViewServlet 的 Servlet,这个 Servlet 可以开启 Druid 的内置监控页面功能, 展示 Druid 的统计信息,它的主要用途可以提供监控信息展示的 html 页面、提供监控信息的 JSON API等。StatViewServlet 是一个标准的 javax.servlet.http.HttpServlet,想要开启 Druid 的内置监控页面,我们可以在配置类中,通过 ServletRegistrationBean 将 StatViewServlet 注册到容器中,来开启 Druid 的内置监控页面。

spring:
  datasource:
    druid:
      stat-view-servlet:
        enabled: true                   # 启用StatViewServlet
        url-pattern: /druid/*           # 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
        reset-enable: false             # 不允许清空统计数据,重新计算
        loginUsername: root             # 配置监控页面访问用户
        login-password: 123             # 配置监控页面访问密码
        allow: 127.0.0.1                # 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
        deny:                           # 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝

  启动 Spring Boot 项目后,浏览器访问 http://localhost:8080/druid/login.html ,即可访问 Druid 的内置监控页面的登录页,如下图所示。
在这里插入图片描述

  在登录页中,分别输入自定义的用户名和密码,用户和密码对应我们的配置文件中设置的用户名(loginUsername)和密码(loginPassword)。输入用户名密码登录进去可以看到里面有很多监控,如下所示:

在这里插入图片描述

2.4.2 数据源

  数据源页面是当前DataSource配置的基本信息,上述配置的Filter可以在里面找到,如果没有配置Filter(一些信息会无法统计,例如“SQL监控”,会无法获取JDBC相关的SQL执行信息)
在这里插入图片描述

2.4.3 SQL 监控

  Druid 内置提供了一个 StatFilter,通过它可以开启 Druid 的 SQL 监控功能,对 SQL 进行监控。SQL监控页面统计了所有SQL语句的执行情况,执行时间、读取行数、更新行数都有区间分布,如下图所示。

在这里插入图片描述

  StatFilter 的别名是 stat,这个别名的映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置_StatFilter 中给出了在 Spring 中配置该别名(stat)开启 Druid SQL 监控的方式,配置如下。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
	... ...
	<property name="filters" value="stat" />
</bean>

  根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“stat”的“filters”属性,就能开启 Druid SQL 监控。

spring:
  datasource:
    druid:
      filter:
        stat:
          enabled: true         # 开启DruidDataSource状态监控
          db-type: mysql        # 数据库的类型
          log-slow-sql: true    # 开启慢SQL记录功能
          slow-sql-millis: 2000 # 默认3000毫秒,这里超过2s,就是慢,记录到日志

2.4.4 Web 应用

  Druid 还内置提供了一个名为 WebStatFilter 的过滤器,它可以用来监控与采集 web-jdbc 关联监控的数据。根据 Druid 官方文档-配置_配置WebStatFilter,想要开启 Druid 的 Web-JDBC 关联监控,只需要将 WebStatFilter 配置在 Web 应用中的 WEB-INF/web.xml 中即可,web.xml 配置如下。

<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>
</filter>
<filter-mapping>
    <filter-name>DruidWebStatFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

  Spring Boot 项目中是没有 WEB-INF/web.xml 的,但是我们可以在配置类中,通过 FilterRegistrationBean 将 WebStatFilter 注入到容器中,来开启 Druid 的 Web-JDBC 关联监控。

spring:
  datasource:
    druid:
      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 的内置监控页面,切换到 Web 应用模块,可以看到 Druid 的 Web 监控已经开启,与此同时,URI 监控和 Session 监控也都被开启。

2.4.5 URL 监控

URL监控页面统计了所有Controller接口的访问以及执行情况。这里可以很清晰的看到,每个url涉及到的数据库执行的信息。

在这里插入图片描述

2.4.6 Session 监控

Session监控页面 可以看到当前的session状况,创建时间、最后活跃时间、请求次数、请求时间等详细参数。

2.4.7 Spring 监控

Spring 监控页面,利用aop 对指定接口的执行时间,jdbc数进行记录

在这里插入图片描述

访问之后 Spring 监控默认是没有数据的,Spring 监控是用于通过 aop 切面对指定类及方法进行监控,这需要导入SprngBoot的AOP的Starter,如下所示:

<!--SpringBoot 的aop 模块-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后在 application.yml 配置 Spring 监控 AOP 切入点,如 com.springboot.template.dao.*,配置多个英文逗号分隔,例如如下所示:

spring:
  datasource:
    druid:
      aop-patterns:"com.springboot.template.dao.*" # Spring监控AOP切入点,多个时用英文逗号分隔

在这里插入图片描述

2.4.8 SQL 防火墙

  Druid 内置提供了一个 WallFilter,使用它可以开启防火墙功能,防御 SQL 注入攻击。WallFilter 的别名是 wall,这个别名映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置 wallfilter 中给出了在 Spring 中使用该别名(wall)开启防火墙的方式,配置如下。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    ... ...
    <property name="filters" value="wall" />
</bean>

  WallFilter 可以结合其他 Filter 一起使用,例如:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    ...
    <property name="filters" value="wall,stat"/>
</bean>

  根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“wall”的“filters”属性,就能开启 Druid 的防火墙功能,因此我们只需要在配置类中为 dataSource 的 filters 属性再添加一个“wall”即可(多个属性值之间使用逗号“,”隔开),代码如下。

spring:
  datasource:
    druid:
      filters: stat,wall,slf4j

  访问 Druid 的内置监控页面,切换到 SQL 防火墙,可以看到 Druid 防火墙已经开启。SQL防火墙页面 druid提供了黑白名单的访问,可以清楚的看到sql防护情况。

2.4.9 JSON API

JSONAPI 页面 通过api的形式访问Druid的监控接口,api接口返回Json形式数据。

三、自定义DruidDataSource

3.1 去广告

  访问监控页面的时候,可能会在页面底部(footer)看到内置的广告,如下所示。这是因为引入的druid的 druid-x.y.z.jar 包中的 common.js(里面有一段js代码是给页面的footer追加广告的)。如果想去掉,有两种方式:
在这里插入图片描述

  • 直接手动注释这段代码

      如果是使用Maven,直接到本地仓库中,查找这个 druid-x.y.z.jar 包,然后用压缩工具打开,找到 support/http/resources/js/common.js,然后注释掉里面的代码 或者修改为自己的页脚内容。

    // this.buildFooter();
    

    在这里插入图片描述

  • 使用过滤器过滤

    注册一个过滤器,过滤common.js的请求,使用正则表达式替换相关的广告内容,如下代码所示:

    @Configuration
    @ConditionalOnWebApplication
    @AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
    @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled",
    havingValue = "true", matchIfMissing = true)
    public class RemoveDruidAdConfig {
    
        /**
        * 除去页面底部的广告
        */
        @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;
        }
    }
    

两种方式都可以,建议使用的是第一种,从根源解决。

3.2 取 Druid 的监控数据

  Druid 的监控数据不仅可以在页面上查看,在 开启 StatFilter 后 ,也可以通过 DruidStatManagerFacade 类进行获取。其中 getDataSourceStatDataList 方法可以获取所有数据源的监控数据,除此之外 DruidStatManagerFacade 还提供了一些其他方法,可以按需选择使用。

@RestController
@RequestMapping(value = "/druid")
public class DruidStatController {
  /**
     * Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可
     */
    @Resource
    private DataSource dataSource;
    
    /**
     * 获取 druid 数据监控信息,其中统计了所有数据源的所有详细信息。
     */
    @GetMapping("/stat")
    public List<Map<String, Object>> druidStat(){
        // 获取数据源的监控数据
        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    }
    
    /**
     * 使用 DruidDataSource API 获取具体数据源的指定的监控信息。
     */
    @GetMapping("/druidDataSource")
    public Map<String, Object> druidDataSource() {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("class", dataSource.getClass());
        if (dataSource instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            dataMap.put("version", druidDataSource.getVersion());
            dataMap.put("name", druidDataSource.getName());
            dataMap.put("initialSize", druidDataSource.getInitialSize());
            dataMap.put("maxActive", druidDataSource.getMaxActive());
            dataMap.put("minIdle", druidDataSource.getMinIdle());
            dataMap.put("activeCount", druidDataSource.getActiveCount());
            dataMap.put("activePeak", druidDataSource.getActivePeak());
            dataMap.put("activePeakTime", druidDataSource.getActivePeakTime());
            dataMap.put("poolingCount", druidDataSource.getPoolingCount());
            dataMap.put("poolingPeak", druidDataSource.getPoolingPeak());
            dataMap.put("poolingPeakTime", druidDataSource.getPoolingPeakTime());
        }
        return dataMap;
    }
}

获取 druid 数据监控信息

参数名描述
ActiveCount当前连接池中活跃连接数
ActivePeak连接池中活跃连接数峰值
ActivePeakTime活跃连接池峰值出现的时间
BlobOpenCountBlob 打开数
ClobOpenCountClob 打开数
CommitCount提交数
DbType数据库类型
DefaultAutoCommit是否默认提交
DiscardCount放弃/丢弃的个数
DriverClassName驱动类
ErrorCount错误数
ExceptionSorterClassName异常分类器类名
ExecuteBatchCount执行批次计数
ExecuteCount执行总数
ExecuteQueryCount执行查询计数
ExecuteUpdateCount执行更新计数
FailFast是否快速失败
FilterClassNames过滤器类名称
Identity
InitGlobalVariants初始全局变量
InitVariants初始变量
InitialSize连接池建立时创建的初始化连接数
KeepAlive是否保持活跃
KeepAliveCheckCount保持活动检查计数
LogDifferentThread记录不同的线程
LogicCloseCount产生的逻辑连接关闭总数
LogicConnectCount产生的逻辑连接建立总数
LogicConnectErrorCount产生的逻辑连接出错总数
LoginTimeout数据库客户端登录超时时间
MaxActive连接池中最大的活跃连接数
MaxEvictableIdleTimeMillis最大可收回空闲时间(毫秒)
MaxWait最大等待时间
MaxWaitThreadCount最大等待线程计数
MinEvictableIdleTimeMillis最小可执行时间Mill
MinIdle连接池中最小的活跃连接数
Name数据源名称
NotEmptyWaitCount获取连接时最多等待多少次
NotEmptyWaitMillis获取连接时最多等待多长时间,毫秒为单位
PSCacheAccessCountPSCache访问总数
PSCacheHitCountPSCache命中次数
PSCacheMissCountPSCache未命中次数
PhysicalCloseCount产生的物理关闭总数
PhysicalConnectCount产生的物理连接建立总数
PhysicalConnectErrorCount产生的物理连接失败总数
PoolPreparedStatements预编译准备语句
PoolingCount当前连接池中的连接数
PoolingPeak连接池中连接数的峰值
PoolingPeakTime连接池数目峰值出现的时间
PreparedStatementClosedCount预编译准备语句关闭计数
PreparedStatementOpenCount准备语句打开计数
QueryTimeout查询超时数
RecycleErrorCount回收错误计数
RemoveAbandoned已删除
RollbackCount回滚数
StartTransactionCount事务开始的个数
TestOnBorrow
TestOnReturn
TestWhileIdle
TransactionQueryTimeout事务查询超时数
URL数据库连接地址
UseUnfairLock使用不公平锁定
UserName数据库连接账号
WaitThreadCount当前等待获取连接的线程数

3.3 使用log4j2进行日志输出

  1. log4j2.xml文件中的日志配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="OFF">
        <appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <!--只接受程序中DEBUG级别的日志进行处理-->
                <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            </Console>
            <!--处理DEBUG级别的日志,并把该日志放到logs/debug.log文件中-->
            <!--打印出DEBUG级别日志,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
            <RollingFile name="RollingFileDebug" fileName="./logs/debug.log"
                         filePattern="logs/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="DEBUG"/>
                    <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
            <!--处理INFO级别的日志,并把该日志放到logs/info.log文件中-->
            <RollingFile name="RollingFileInfo" fileName="./logs/info.log"
                         filePattern="logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <!--只接受INFO级别的日志,其余的全部拒绝处理-->
                    <ThresholdFilter level="INFO"/>
                    <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
            <!--处理WARN级别的日志,并把该日志放到logs/warn.log文件中-->
            <RollingFile name="RollingFileWarn" fileName="./logs/warn.log"
                         filePattern="logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="WARN"/>
                    <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
            <!--处理error级别的日志,并把该日志放到logs/error.log文件中-->
            <RollingFile name="RollingFileError" fileName="./logs/error.log"
                         filePattern="logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
                <ThresholdFilter level="ERROR"/>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
            <!--druid的日志记录追加器-->
            <RollingFile name="druidSqlRollingFile" fileName="./logs/druid-sql.log"
                         filePattern="logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
        </appenders>
        <loggers>
            <root level="DEBUG">
                <appender-ref ref="Console"/>
                <appender-ref ref="RollingFileInfo"/>
                <appender-ref ref="RollingFileWarn"/>
                <appender-ref ref="RollingFileError"/>
                <appender-ref ref="RollingFileDebug"/>
            </root>
            <!--记录druid-sql的记录-->
            <logger name="druid.sql.Statement" level="debug" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            </logger>
            <logger name="druid.sql.Statement" level="debug" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            </logger>
            <!--log4j2 自带过滤日志-->
            <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
            <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
            <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
            <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
            <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
            <Logger name="org.crsh.plugin" level="warn" />
            <logger name="org.crsh.ssh" level="warn"/>
            <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
            <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
            <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
            <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
            <logger name="org.thymeleaf" level="warn"/>
        </loggers>
    </configuration>
    
  2. 配置 application.properties

    # 配置日志输出
    spring.datasource.druid.filter.slf4j.enabled=true
    spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独泪了无痕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值