• SpringBoot(13)---整合Druid实现多数据源和可视化监控


    SpringBoot整合Druid实现多数据源和可视化监控

    先献上github代码地址:https://github.com/yudiandemingzi/spring-boot-many-data-source
    代码拉下来换下自己的mysql数据库地址,就可以直接运行。Druid的优点很明显,它的可视化界面可以监控Sql语句和URI执行情况在开发中真的很需要。
    先说优点吧:

        1) 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
        2) 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。
        3) 可以监控数据库访问性能,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
        4) SQL执行日志,Druid提供了不同的LogFilter,监控你应用的数据库   访问情况。
        5)扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter-Chain机制,很方便编写JDBC层的扩展插件。
    

    二、配置多数据源

    1、pom.xml

    只需要添加druid这一个jar就行了,有关springboot项目他还有个整合包,用那个整合包也一样。

           <!-- Druid 数据连接池依赖 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.8</version>
            </dependency>
    
            <dependency>
    			<groupId>com.alibaba</groupId>
    			<artifactId>druid-spring-boot-starter</artifactId>
    			<version>1.1.6</version>
    		</dependency>
    

    对于springboot项目来讲,上面任选一个都是可以的,亲测有效。

    2、application.yml

    我这里是采用application.yml进行添加配置,这里面配置了两个数据源,其实在application.yml也可以不配置这些东西,它的主要作用是给数据源配置类读取数据用的。

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        initialSize: 10
        minIdle: 10
        maxActive: 200
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 30000
        validationQuery: select 'x'
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        # 配置监控统计拦截的filters
        filters: stat,wall,slf4j
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
    
    #配置了两个数据源
    master:
      datasource:
        url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
        username: root
        password: root
        driverClassName: com.mysql.jdbc.Driver
    
    cluster:
      datasource:
        url: jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
        username: root
        password: root
        driverClassName: com.mysql.jdbc.Driver
    

    3、主数据源配置类(MasterDataSourceConfig)

    /**
     * 主数据源配置
     */
    @Configuration
    @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
    public class MasterDataSourceConfig {
    
        /**
         * 配置多数据源 关键就在这里 这里配置了不同的数据源扫描不同mapper
         */
        static final String PACKAGE = "com.binron.multidatasource.mapper.master";
        static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";
    
        /**
         * 连接数据库信息 这个其实更好的是用配置中心完成
         */
        @Value("${master.datasource.url}")
        private String url;  
          
        @Value("${master.datasource.username}")
        private String username;  
          
        @Value("${master.datasource.password}")
        private String password;  
          
        @Value("${master.datasource.driverClassName}")
        private String driverClassName;
    
    
        /**
         * 下面的配置信息可以读取配置文件,其实可以直接写死 如果是多数据源的话 还是考虑读取配置文件
         */
        @Value("${spring.datasource.initialSize}")
        private int initialSize;  
          
        @Value("${spring.datasource.minIdle}")
        private int minIdle;  
          
        @Value("${spring.datasource.maxActive}")
        private int maxActive;  
          
        @Value("${spring.datasource.maxWait}")
        private int maxWait;  
          
        @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
        private int timeBetweenEvictionRunsMillis;  
          
        @Value("${spring.datasource.minEvictableIdleTimeMillis}")
        private int minEvictableIdleTimeMillis;  
          
        @Value("${spring.datasource.validationQuery}")
        private String validationQuery;  
          
        @Value("${spring.datasource.testWhileIdle}")
        private boolean testWhileIdle;  
          
        @Value("${spring.datasource.testOnBorrow}")
        private boolean testOnBorrow;  
          
        @Value("${spring.datasource.testOnReturn}")
        private boolean testOnReturn;  
          
        @Value("${spring.datasource.poolPreparedStatements}")
        private boolean poolPreparedStatements;  
          
        @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
        private int maxPoolPreparedStatementPerConnectionSize;  
          
        @Value("${spring.datasource.filters}")
        private String filters;
          
        @Value("{spring.datasource.connectionProperties}")
        private String connectionProperties;  
        
        
        @Bean(name = "masterDataSource")
        @Primary //标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。
        public DataSource masterDataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(url);  
            dataSource.setUsername(username);  
            dataSource.setPassword(password);  
            dataSource.setDriverClassName(driverClassName);  
              
            //具体配置 
            dataSource.setInitialSize(initialSize);  
            dataSource.setMinIdle(minIdle);  
            dataSource.setMaxActive(maxActive);  
            dataSource.setMaxWait(maxWait);  
            dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);  
            dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);  
            dataSource.setValidationQuery(validationQuery);  
            dataSource.setTestWhileIdle(testWhileIdle);  
            dataSource.setTestOnBorrow(testOnBorrow);  
            dataSource.setTestOnReturn(testOnReturn);  
            dataSource.setPoolPreparedStatements(poolPreparedStatements);  
            dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
    
            /**
             * 这个是用来配置 druid 监控sql语句的 非常有用 如果你有两个数据源 这个配置哪个数据源就监控哪个数据源的sql 同时配置那就都监控
             */
            try {
                dataSource.setFilters(filters);
            } catch (SQLException e) {
            	e.printStackTrace();
            }
            dataSource.setConnectionProperties(connectionProperties);  
            return dataSource;
        }
    
        @Bean(name = "masterTransactionManager")
        @Primary
        public DataSourceTransactionManager masterTransactionManager() {
            return new DataSourceTransactionManager(masterDataSource());
        }
    
        @Bean(name = "masterSqlSessionFactory")
        @Primary
        public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
                throws Exception {
            final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
            sessionFactory.setDataSource(masterDataSource);
            sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
            
            return sessionFactory.getObject();
        }
    }
    

    这里说明几点

      1) @Primary: 多数据源配置的时候注意,必须要有一个主数据源, 用 @Primary 标志该 Bean。标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean优先被考虑。
      2) dataSource.setFilters(filters): 这个是用来配置 druid 监控sql语句的, 如果你有两个数据源 这个配置哪个数据源就监控哪个 数据源的sql,同时配置那就都监控。
      3) 能够做到多个数据源的关键点 就是每个数据源所扫描的mapper包不一样,谁扫描到哪个mapper那么该mapper就用哪个数据源,同时都扫到了呢,
          那当然就得用主数据源咯,也就是添加@Primary 的数据源。
    

    4、次数据源(ClusterDataSourceConfig)

    这里省略了部分代码,因为和主是一样的,完整代码在github代码里有。

    /**
     * 次数据源 另一个数据源配置
     */
    @Configuration
    @MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "clusterSqlSessionFactory")
    public class ClusterDataSourceConfig {
    
        /**
         * 配置多数据源 关键就在这里 这里配置了不同数据源扫描不同的mapper
         */
        static final String PACKAGE = "com.binron.multidatasource.mapper.cluster";
        static final String MAPPER_LOCATION = "classpath:mapper/cluster/*.xml";
    
        @Value("${cluster.datasource.url}")
        private String url;
    
        @Value("${cluster.datasource.username}")
        private String username;
    
        @Value("${cluster.datasource.password}")
        private String password;
    
        @Value("${cluster.datasource.driverClassName}")
        private String driverClass;
    
        @Bean(name = "clusterDataSource")
        public DataSource clusterDataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            dataSource.setDriverClassName(driverClass);
    
            //具体配置
            try {
                dataSource.setFilters("stat,wall,slf4j");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return dataSource;
        }
    
        @Bean(name = "clusterTransactionManager")
        public DataSourceTransactionManager clusterTransactionManager() {
            return new DataSourceTransactionManager(clusterDataSource());
        }
    
        @Bean(name = "clusterSqlSessionFactory")
        public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource clusterDataSource)
                throws Exception {
            final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
            sessionFactory.setDataSource(clusterDataSource);
    
            sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ClusterDataSourceConfig.MAPPER_LOCATION));
            return sessionFactory.getObject();
        }
    }
    

    这里说明几点:

       1)发现次数据源所扫描的mapper和主是完全不一样的,说明每个数据源负责自己的mapper
       2)  次数据源是没有加@Primary。
       3)这里也添加了dataSource.setFilters(filters):说明 次数据源也需要监听sql语句。
    

    三、配置可视化界面

    直接上代码:

    /**
     * druid监控界面设置
     */
    @Configuration
    public class DruidConfiguration {
    
        @Bean
        public ServletRegistrationBean druidStatViewServle() {
            //注册服务
            ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                    new StatViewServlet(), "/druid/*");
            // 白名单(为空表示,所有的都可以访问,多个IP的时候用逗号隔开)
            servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
            // IP黑名单 (存在共同时,deny优先于allow) (黑白名单就是如果是黑名单,那么该ip无法登陆该可视化界面)
            servletRegistrationBean.addInitParameter("deny", "127.0.0.2");
            // 设置登录的用户名和密码
            servletRegistrationBean.addInitParameter("loginUsername", "root");
            servletRegistrationBean.addInitParameter("loginPassword", "123456");
            // 是否能够重置数据.
            servletRegistrationBean.addInitParameter("resetEnable", "false");
            return servletRegistrationBean;
        }
    
        @Bean
        public FilterRegistrationBean druidStatFilter() {
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
                    new WebStatFilter());
            // 添加过滤规则
            filterRegistrationBean.addUrlPatterns("/*");
            // 添加不需要忽略的格式信息
            filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
            System.out.println("druid初始化成功!");
            return filterRegistrationBean;
    
        }
    }
    

    这里说明几点

    (1)登陆地址:http://127.0.0.1:8080/druid/index.html(端口号看自己设置的端口号)
    (2)一旦配置黑名单,那么该ip访问是没有权限的登陆的
    (3)如果想看SQL执行结果,那么上面数据源配置一定要添加dataSource.setFilters(filters),我之前就没有添加,所以其它都能正常使用,就是无法监控Sql语句。
    

    效果图

    确实很好用。顺便讲下,我遇到其它功能都有用,唯独Sql语句无法监控的问题,之后看界面才看出端倪来。

    我发现界面中,filter类名为空,可是在yml确实配置了filters,怎么还是空,原因是数据源里没有配置setFilters(filters)


    文献资料

    1、 阿里github有关Druid结合Boot文档
    2、 demo参考github地址: (感谢作者分享)
    3、 数据连接池的属性字段说明
    4、 yml配置、properties配置、ssm配置

  • 相关阅读:
    (8)闭包函数(函数的传参方式)
    (7)名称空间和作用域
    (6)函数嵌套
    (5)函数对象
    (4)进阶函数(作用域、闭包、生成器、迭代器)
    (3)什么是函数(函数的定义、形参、实参、默认形参、可变长函数args kwargs,私有地址)
    (1)三元运算、字符编码
    (2)字符编码关系和转换(bytes类型)
    java技术学习网址收藏
    springmvc工作原理和环境搭建
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/10192041.html
Copyright © 2020-2023  润新知