• Springboot——整合Druid、mybatisPlus实现SQL监控


    前言

    Springboot项目应用中,通常连接数据库时,会直接/间接引入一个org.springframework.boot spring-boot-starter-jdbc 依赖 pom 。默认使用HikariCP 数据库连接池。

    虽然HikariCP 具有效率高的特点之外,与Druid相比,还有很多的不足。

    Druid 连接池的特点

    Druid数据库连接池,能够在除了配置数据库连接线程池之外,还能进行SQL运行监控和其他扩展功能。

    其各个配置中的亮点:

    • stat:Druid内置提供一个StatFilter,用于统计监控信息。
    • wall:Druid防御SQL注入攻击的WallFilter就是通过Druid的SQL Parser分析。

      Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。

    • log4j2:这个就是 日志记录的功能,可以把sql语句打印到log4j2 供排查问题。

    项目测试和配置

    接下来进行单Springboot项目,整合DruidMybatisPlus等框架实现监控演示。

    测试框架版本

    • Springboot 2.1.4.RELEASE
    • Druid 1.1.23
    • Mybatis-plus 3.4.0

    依赖引入

    根据上面的框架要求,选择对应版本的pom依赖文件进行配置。

    主要依赖信息如下所示:

    <!-- 阿里巴巴的druid数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.23</version>
    </dependency>
    <!-- mysql8 驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.0</version>
        <exclusions>
            <!-- 排除默认的 HikariCP 数据源 -->
            <exclusion>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--
    mybatis,引入了 SpringBoot的 JDBC 模块,
    所以,默认是使用 hikari 作为数据源
    -->
    <!--<dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.3</version>
        <exclusions>
            <exclusion>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
            </exclusion>
        </exclusions>
    </dependency>-->
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    【注意】为什么需要移除 HikariCP ?

    因为Springboot项目应用中,通常连接数据库时,会直接/间接引入一个org.springframework.boot spring-boot-starter-jdbc 依赖 pom,其中默认的连接池为 HikariCP
    使用Druid 连接池,则需要将其他默认连接池移除!

    配置Druid连接池

    主要配置数据库连接信息、监控信息等。其配置application.yml如下所示:

    spring:
      datasource:
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver   # mysql8 的连接驱动
        url: jdbc:mysql://106.55.137.66:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
        platform: mysql # 数据库类型
        type: com.alibaba.druid.pool.DruidDataSource  # 指定数据源类型
        ### 配置 druid 数据库连接池
        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: "cn.xj.dao.*"
          ########### 启用内置过滤器(第一个 stat必须,否则监控不到SQL)##########
          filters: stat,wall,log4j2
          # 自己配置监控统计拦截的filter
          filter:
            # 开启druiddatasource的状态监控
            stat:
              enabled: true
              db-type: mysql
              # 开启慢sql监控,超过5s 就认为是慢sql,记录到日志中
              log-slow-sql: true
              slow-sql-millis: 5000
            # 日志监控,使用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关联监控的数据 ##########
          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
          ########## 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ##########
          stat-view-servlet:
            enabled: true                   # 启用StatViewServlet
            url-pattern: /druid/*           # 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
            reset-enable: false              # 不允许清空统计数据,重新计算
            
            login-username: root            # 配置监控页面访问账户和密码
            login-password: 123             # druid 的web界面为 /druid/login.html
            
            allow: 127.0.0.1           # 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
            deny:                                        # 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝
    
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    配置mybatisPlus

    关于数据库表和类等创建,这里不做重复描述,可以参考下列文章:

    Mybatis-Plus 看这一篇就够了

    关于druid 的 web 界面去AD配置

    访问监控页面的时候,你可能会在页面底部(footer)看到阿里巴巴的广告
    原因:引入的druid的jar包中的common.js(里面有一段js代码是给页面的footer追加广告的)

    如果需要去除,可以增加以下配置类:

    import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
    import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
    import com.alibaba.druid.util.Utils;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    @Configuration
    @ConditionalOnWebApplication
    @AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
    @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled",
            havingValue = "true", matchIfMissing = true)
    public class RemoveDruidAdConfig {
    
        /**
         * 方法名: 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    资料参考

    玩转 SpringBoot 监控统计

  • 相关阅读:
    作用域的概念及作用?作用域的分类?.js 属于哪种作用域?
    【Linux】《Linux命令行与shell脚本编程大全 (第4版) 》笔记-Chapter21-sed 进阶
    SQL引擎子系统的工作原理
    如何在C程序中使用libcurl库下载网页内容
    探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty
    centos7 install postgres-15
    c语言编程 “画圆” 源程序
    C++ 即将超越 Java,TIOBE 6 月编程语言排行榜发布!
    IDEA 基本配置
    【音视频|ALSA】ALSA是什么?ALSA框架详细介绍
  • 原文地址:https://blog.csdn.net/qq_38322527/article/details/125405003