druid统计参数变化的代码实现

druid统计参数变化的代码实现

1、背景

​ 源码阅读已有一周,阅读花费时间长的可能就两天吧,其他基本上没有看很久,周末分享实在拿不出啥东西来,所以选择一个小的点进行分享,以自己的体验为主。

2、环境

springboot + mysql + druid1.2.8 + maven3.6.3 + jdk1.8 + idea

本来是想着能否用servlet直接集成druid进行run和测试,由于时间关系,研究一半中断了,就采用平时用的多的springboot

3、目标说明

druid以监控统计著称,所以希望找到druid是怎么来加统计信息的,比如,insert之后统计页面上的执行数怎么发生变化的。当然也包括其他的比如查询、sql记录、执行时间、这些统计数据怎么存储等这些

4、开始跑项目

代码很简单,配置好application.yaml先:

spring:
  datasource:
    username: root
    password: root
    #serverTimezone=UTC 配置时区
    url: jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

注册ServletRegistrationBean和FilterRegistrationBean

package com.test.druidstat;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.AbstractWebStatImpl;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid(){
        return new DruidDataSource();
    }

    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
        Map<String,String> initParams = new HashMap<>();
        initParams.put("loginUsername","admin");
        initParams.put("loginPassword","123456");
        initParams.put("allow",""); //默认就是允许所有访问
        //deny:Druid 后台拒绝谁访问,表示禁止此ip访问
        // initParams.put("deny","192.168.10.132");
        bean.setInitParameters(initParams);
        return bean;
    }



    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        Map<String,String> initParams = new HashMap<>();
        initParams.put("exclusions","*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }
}

添加测试类:

package com.test.druidstat;

import com.alibaba.druid.stat.DruidStatManagerFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/")
public class TestController {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @GetMapping("userList")
    public List<Map<String, Object>> userList(){
        String sql = "select * from user_innodb limit 1, 10";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }

    @GetMapping("insert")
    public int insert(){
        String sql = "insert into user_innodb (name, gender, phone) values ('test', '1', '13813813811')";
        return jdbcTemplate.update(sql);
    }

    @GetMapping("/stat")
    public Object druidStat() {
        // DruidStatManagerFacade#getDataSourceStatDataList 该方法可以获取所有数据源的监控数据
        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    }

}

先访问http://127.0.0.1:8080/druid登陆控制台:

在这里插入图片描述

访问http://127.0.0.1:8080/userList和http://127.0.0.1:8080/insert进行测试:

在这里插入图片描述

会发现控制台的sql监控会发生变化,利用测试的请求断点调试。

5、走的弯路

打断点发现,堆栈信息非常大,很难找到具体的实现在哪:
在这里插入图片描述

就想先找找请求链接实现在哪:

先看控制台的请求:

在这里插入图片描述

使用双击shift查找/sql.json

在这里插入图片描述

不幸的是并没有找到任何东西,无奈只能去看源码目录找长得像的,发现MonitorStatService.java:

在这里插入图片描述

然而在集成druid中jar内并没有这个package和class:(没有com.alibaba.druid.admin这个包)

在这里插入图片描述

无奈,只能看别的类,最后发现了TableStat类,看到了insertCount以为知道了字段(因为先测试的插入sql):

在这里插入图片描述

然而打断点并没有进来!

6、初步发现端倪

经过上述的尝试可以发现,插入sql带来改变的变量并不是insertCount,因为那是唯一一个增长的方法。所以盲目靠猜和看的办法行不通,只能回到断点调试,仔细观察,最终将范围缩小定位到WebStatFilter:

在这里插入图片描述

最后定位到this.webAppStat.afterInvoke():

在这里插入图片描述

可以看到具体的变量改变出来了

继续跟踪:

在这里插入图片描述

发现初始化统计参数是在beforeInvoke实现的,并且是先存在sessionStatMap中,并能够知道是通过LRU缓存来获取的:

在这里插入图片描述

可以看到具体的变量改变出来了

继续跟踪:

在这里插入图片描述

发现初始化统计参数是在beforeInvoke实现的,并且是先存在sessionStatMap中,并能够知道是通过LRU缓存来获取的:

在这里插入图片描述

后续再研究!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值