Springboot+Druid 動態數據源配置監控

一、引入maven依賴,使用 starter 與原生 druid 依賴配置有所不同

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

二、配置數據源

spring:
  datasource:
    druid:
      filter:
        stat: #開啓sql監控
          enabled: true
        wall:
          enabled: true
        slf4j:
          enabled: true
      DB1:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:sqlserver://127.0.0.1:1487;DatabaseName=CN2018
        username: root
        password: 12345
        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
        # 連接池的配置信息
        initial-size: 5
        min-idle: 5
        maxActive: 20
        maxWait: 60000 # 配置獲取連接等待超時的時間
        timeBetweenEvictionRunsMillis: 60000   # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
        minEvictableIdleTimeMillis: 300000     # 配置一個連接在池中最小生存的時間,單位是毫秒
#        validationQuery: SELECT 1 FROM DUAL   # mysql數據庫
        validationQuery: SELECT 1              # sqlserver 數據庫
        poolPreparedStatements: true           # 打開PSCache,並且指定每個連接上PSCache的大小
        maxPoolPreparedStatementPerConnectionSize: 20
      DB2:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:sqlserver://127.0.0.1:1488;DatabaseName=DB2022
        username: root
        password: 123456
        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
        # 連接池的配置信息
        initial-size: 1
        min-idle: 1
        maxActive: 5
        maxWait: 60000 # 配置獲取連接等待超時的時間
        timeBetweenEvictionRunsMillis: 60000  # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
        minEvictableIdleTimeMillis: 300000    # 配置一個連接在池中最小生存的時間,單位是毫秒
#        validationQuery: SELECT 1 FROM DUAL  # mysql數據庫
        validationQuery: SELECT 1             # sqlServer 數據庫
        poolPreparedStatements: true          # 打開PSCache,並且指定每個連接上PSCache的大小
        maxPoolPreparedStatementPerConnectionSize: 20

三、創建配置類

//記錄數據庫名
public interface ContextConst {
    enum DataSourceType{
        DB1,DB2
    }
}
//數據源持有類
@Slf4j
public class DataSourceContextHolder {
    /**
     * CONTEXT_HOLDER代表一個可以存放String類型的ThreadLocal對象,
     * 此時任何一個線程可以併發訪問這個變量,
     * 對它進行寫入、讀取操作,都是線程安全的。
     * 比如一個線程通過CONTEXT_HOLDER.set(“aaaa”);將數據寫入ThreadLocal中,
     * 在任何一個地方,都可以通過CONTEXT_HOLDER.get();將值獲取出來。
     * 這裏寫入的就是數據庫名,
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSource(String dbType){
        CONTEXT_HOLDER.set(dbType);
    }

    public static String getDataSource(){
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource(){
        CONTEXT_HOLDER.remove();
    }
}
//數據源路由實現類
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * @Description:數據源路由實現類 AbstractRoutingDataSource(每執行一次數據庫 , 動態獲取DataSource)
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}
//自定義切換數據源註解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDateSouce {
    ContextConst.DataSourceType value() default ContextConst.DataSourceType.DB1;
}
//AOP動態數據源通知
@Component
@Aspect
@Order(-1) //保證在@Transactional之前執行,必須加上,不然無法分辨是哪個數據源在執行事務
@Slf4j
public class DynamicDataSourceAspect {

    @Before("execution(* com.blaze.pboc.service..*.*(..))")
    public void before(JoinPoint point) {
        try {
            TargetDateSouce annotationOfClass = point.getTarget().getClass().getAnnotation(TargetDateSouce.class);
            String methodName = point.getSignature().getName();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            TargetDateSouce methodAnnotation = method.getAnnotation(TargetDateSouce.class);
            methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;
            ContextConst.DataSourceType dataSourceType = methodAnnotation != null && methodAnnotation.value() != null ? methodAnnotation.value() : ContextConst.DataSourceType.DB1;
            DataSourceContextHolder.setDataSource(dataSourceType.name());
        } catch (NoSuchMethodException e) {
            log.error("error", e);
        }
    }

    @After("execution(* com.blaze.pboc.service..*.*(..))")
    public void after(JoinPoint point) {
        DataSourceContextHolder.clearDataSource();
    }
}
//動態數據源配置類
@SpringBootConfiguration
public class DruidDataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
    public DruidDataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
    public DruidDataSource clusterDataSource() {
        DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
        return druidDataSource;
    }

    @Primary
    @Bean
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //配置默認數據源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        //配置多數據源這裏的key一定要是string類型,枚舉類型並不支持,所以用到枚舉中name()方法轉成string,或者用toString方法。
        HashMap<Object, Object> dataSourceMap = new HashMap();
        dataSourceMap.put(ContextConst.DataSourceType.DB1.name(), masterDataSource());
        dataSourceMap.put(ContextConst.DataSourceType.DB2.name(), clusterDataSource());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }

    // 配置@Transactional註解事務
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

    //配置 Druid 監控管理後臺的Servlet;
    //內置 Servlet 容器時沒有web.xml文件,所以使用 Spring Boot 的註冊 Servlet 方式
    @Bean
    public ServletRegistrationBean registrationBean() {
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("loginUsername", "admin");
        initParameters.put("loginPassword", "12345");
        bean.setInitParameters(initParameters);
        return bean;
    }

    //去除Druid監控頁面的廣告
    @Bean
    public FilterRegistrationBean removeDruidAdFilter() throws IOException {
        String text = Utils.readFromResource("support/http/resources/js/common.js");
        final String newJs = text.replace("this.buildFooter();", "");
        // 新建一個過濾器註冊器對象
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        // 註冊common.js文件的過濾器
        registration.addUrlPatterns("/druid/js/common.js");
        // 添加一個匿名的過濾器對象,並把改造過的common.js文件內容寫入到瀏覽器
        registration.setFilter((servletRequest, servletResponse, filterChain) -> {
            // 重置緩衝區,響應頭不會被重置
            servletResponse.resetBuffer();
            // 把改造過的common.js文件內容寫入到瀏覽器
            servletResponse.getWriter().write(newJs);
        });
        return registration;
    }

}

四、測試,常用的數據源配置db1,無需添加註解

 

五、登陸 web 監控端

http://127.0.0.1:9000/api/druid/login.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章