1. 程式人生 > 其它 >Springboot+Druid 動態資料來源配置監控

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