Druid监控 + 多数据源配置

先贴一下用的依赖项。


      <!-- druid 数据库连接池-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.2.20</version>
		</dependency>
		<!-- mysql 驱动-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.3.1</version>
		</dependency>
		<!--	多数据源-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>


	<!-- 上面是必须的 -->

	<!--spring-boot-web-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

	<!--	代码生成器插件-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-generator</artifactId>
			<version>3.5.3.1</version>
		</dependency>
	<!-- 模版引擎 -->
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
		</dependency>
	<!--	lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--huttol工具类-->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.8.11</version>
		</dependency>





		

application.yaml

yaml配置文件的配置。

# 数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # ps
      # 主库数据源
      master:
        url: jdbc:mysql://localhost:3306/jzm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
        enabled: false
        url:
        username:
        password:
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置连接超时时间
      connectTimeout: 30000
      # 配置网络超时时间
      socketTimeout: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 设置白名单,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # 控制台管理用户名和密码
        login-username: ruoyi
        login-password: 123456
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

DruidConfig

负责读取yaml文件的数据源配置,生成数据源。还有创建动态数据源容器。另外ServletRegistrationBean 和 FilterRegistrationBean的配置和生成。

package com.example.config;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.util.Utils;
import com.example.config.datasource.DynamicDataSource;
import com.example.enums.DataSourceType;
import com.example.config.properties.DruidProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 org.springframework.context.annotation.Primary;

import javax.servlet.*;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * druid监控 + 多数据源配置
 *
 * @author jzm
 */
@Configuration
public class DruidConfig
{
    // 读取主数据源配置
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource()
    {
        //底层会自动拿到datasource1中的配置,创建一个DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    // 读取从数据源配置(默认关闭)
    @Bean
    // 读取配置文件中然后映射成对应的Bean。
    @ConfigurationProperties("spring.datasource.druid.slave")
    // 在特定条件下根据某个配置bean中的属性决定是否映射
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }


    // 创建动态数据源容器
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);

        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");

        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName        数据源名称
     * @param beanName          bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtil.getBean(beanName); // 从spring容器获得,bean实例
            targetDataSources.put(sourceName, dataSource);
        } catch (Exception e)
        {
        }
    }


    /**
     * @description 注册一个StatViewServlet,进行druid监控页面配置
     * @return servlet registration bean
     */
    @Bean
    public ServletRegistrationBean druidStatViewServlet() {
        //先配置管理后台的servLet,访问的入口为/druid/
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                new StatViewServlet(), "/druid/*");
        // IP白名单 (没有配置或者为空,则允许所有访问)
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名单 (存在共同时,deny优先于allow)
        servletRegistrationBean.addInitParameter("deny", "");
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "123456");
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

    /**
     * 去除监控页面底部的广告
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
    // 只有当这个配置为true是,这个bean才会被创建
    public FilterRegistrationBean removeDruidFilterRegistrationBean(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(javax.servlet.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;
    }





}

DynamicDataSource

就是我们的动态数据源,负责继承和初始化 AbstractRoutingDataSource。还有就是重写determineCurrentLookupKey()这个方法,让druid知道我们当前线程要使用那个数据源从而切换那个数据源。

/**
 * 动态数据源
 *
 * @author jzm
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }


    /**
     * 获取当前线程正在用的数据源
     */
    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }

    public static void setDataSource(String dataSource){
        DynamicDataSourceContextHolder.setDataSourceType(dataSource);
    }

    public static void clearDataSource(){
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

DynamicDataSourceContextHolder

动态数据源上下文容器。

/**
 * 数据源切换处理
 *
 * @author jzm
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

DataSourceType

数据源类型。

/**
 * 数据源
 * 
 * @author jzm
 */
public enum DataSourceType
{
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE
}

DataSource

我们自定义一个注解,标记我们当前注解的方法那个数据源。

/**
 * 自定义多数据源切换注解
 *
 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
 *
 * @author jzm
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切换数据源名称
     */
     DataSourceType value() default DataSourceType.MASTER;
}

DataSourceAsept

我们定义一个切面类,负责获取我们的自定义数据源注解的值,从而在DynamicDataSource中的setDataSource() 方法,设置当前线程用的数据源。然后被determineCurrentLookupKey()方法获取到,从而切换数据源。注意在我们处理中方法的优先级比类的优先级高。


/**
 * 数据源切面类
 *
 * @author jzm
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {

    Logger log = LoggerFactory.getLogger(DataSourceAspect.class);

    @Pointcut("@annotation(com.example.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        MethodSignature signature = (MethodSignature) point.getSignature();
        // 处理方法
        Method method = signature.getMethod();
        // 处理类
        Class<?> methodCls = method.getDeclaringClass();
        DataSource clsDs = methodCls.getAnnotation(DataSource.class);
        
        DataSource meDs = method.getAnnotation(DataSource.class);

        if (meDs == null ) {
            if(clsDs == null){
                log.debug("set datasource is " + DataSourceType.MASTER);
            }else{
                DynamicDataSource.setDataSource(clsDs.value().name());
                log.debug("set datasource is" + clsDs.value().name());
            }
        } else {
            DynamicDataSource.setDataSource(meDs.value().name());
            log.debug("set datasource is " + meDs.value().name());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            log.debug("clean datasource");
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

启动类更改 

加上如下注解

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
// 我们在yaml文件里面已经配置数据源自动配置值。但是,我们想要他加载DruidConfig中的自定义配置类。因此要加这个。
public class DruidMonitorDemoApplication
{

    public static void main(String[] args)
    {
        SpringApplication.run(DruidMonitorDemoApplication.class, args);
    }

}

看到这,是已经把配置都能完了。

测试

我们随便找一张表负责数据库连接测试。

生成对应的service、mapper、controller文件。

这里就展示service实现类的写法。

 我们在对应的service实现里面。对想要切换数据源的方法标记@DataSource注解

@Service
@DataSource(value = DataSourceType.MASTER)
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {


    @Override
    @DataSource(value = DataSourceType.MASTER)
    public List<SysUser> findUserList() {
        return this.baseMapper.selectList(null);
    }


}

然后,启动项目。

访问sql监控页面

http://127.0.0.1:8080/druid/index.html  PS: ip和端口替换成自己的。

会出现一个登录页面。我们输入在DruidConfig中,配置好的用户名和密码。

    @Bean
    public ServletRegistrationBean druidStatViewServlet() {
        //先配置管理后台的servLet,访问的入口为/druid/
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                new StatViewServlet(), "/druid/*");
        // IP白名单 (没有配置或者为空,则允许所有访问)
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // IP黑名单 (存在共同时,deny优先于allow)
        servletRegistrationBean.addInitParameter("deny", "");
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "123456");
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

 登录完毕,出现如下页面,代表我们druid监控配置是没问题的。

但是,这时候由于项目并没有crud操作,因此数据源后面sql监控信息都是空的。

利用postman进行测试。

再打开数据源页面,出现如下所示,代表成功了。

多数据源配置验证

我们在进行curd操作时,嗯出现如下,代表数据源切换应该成功。

  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
druid监控多数据源是通过配置多个数据源来实现的。在配置多数据源时,需要确保至少有一个主数据源,通过在主数据源的Bean上添加@Primary标志来指定。配置多数据源时,可以使用dataSource.setFilters(filters)来配置druid监控SQL语句。如果有多个数据源,则可以根据需要配置监控哪个数据源的SQL语句,或同时监控多个数据源的SQL语句。关键点在于每个数据源扫描的mapper包不同,根据扫描到的mapper来确定使用哪个数据源,如果都扫描到了,那么就使用主数据源。次数据源可以是没有加@Primary注解的数据源,它负责自己的mapper,并且也需要配置dataSource.setFilters(filters)来监听SQL语句。所以,druid监控多数据源配置需要注意以上几点。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [druid监控多数据源1](https://download.csdn.net/download/weixin_35737757/86361208)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot(13)---整合Druid实现多数据源和可视化监控](https://blog.csdn.net/a8892117/article/details/101172449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

本郡主是喵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值