Spring Cloud + Vue 电商项目开发实战(2) ——配置Druid及slf4j

配置Druid

​ 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个,释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。通过数据库连接池能明显提高对数据库操作的性能。

​ Spring Boot默认提供了若干种可用的连接池,默认的数据源是org.apache.tomcat.jdbc.pool.DataSource。Druid是阿里系提供的一个开源连接池,除在连接池之外,还提供了非常优秀的数据库监控和扩展功能。

​ Druid是阿里开源的一个JDBC应用组件,其主要包括3部分:

  • DruidDriver:代理Driver,能够提供基于Filter-Chain模式的插件体系。

  • DruidDataSource:高效可管理的数据库连接池。

  • SQLParser:实用的SQL语法分析。

    通过Druid连接池中间件,可以实现:

  • 监控数据库访问性能。Druid内置了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,对于线上分析数据库访问性能有所帮助。

  • 替换传统的DBCP和C3P0连接池中间件。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。数据库密码加密。直接把数据库密码写在配置文件中,容易导致安全问题。DruidDriver和DruidDataSource都支持PasswordCallback。

  • SQL执行日志。Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。扩展JDBC。如果对JDBC层有编程的需求,可以通过Druid提供的Filter-Chain机制很方便地编写JDBC层的扩展插件。

  1. 在pom.xml文件中添加Druid依赖

    <!--Druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-configuration-processor</artifactId>
      	<optional>true</optional>
    </dependency>
    
  2. 修改application.yaml配置文件,把原有的数据源配置替换成Druid数据源并配置数据源相关参数

    spring:
      datasource:
        name: druidDataSource
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/festmon?characterEncoding=UTF-8&useSSL=false
          username: root
          password: 123456
          filters: stat,wall,slf4j,config                  #配置监控统计拦截的filters,去掉后监控界面SQL无法进行统计,wall用于防火墙。
          max-active: 100            #最大连接数
          initial-size: 1            #初始化大小
          max-wait: 60000            #获取连接等待超时时间
          min-idle: 1                #最小连接数
          time-between-eviction-runs-millis: 60000         #间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒。
          min-evictable-idle-time-millis: 300000           #一个连接在池中最小生存的时间,单位是毫秒。
          validation-query: select 'x'
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
          pool-prepared-statements: true
          max-open-prepared-statements: 50
          max-pool-prepared-statement-per-connection-size: 20
    
  3. Druid Spring Starter简化了很多配置,如果默认配置满足不了需求,还可以自定义配置。创建properties文件夹,并在该文件夹下创建DruidDataSourceProperties.class文件

    package com.springframe.festmon.properties;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "spring.datasource.druid")  //扫描配置类的属性前缀
    public class DruidDataSourceProperties {
    
        // jdbc
        private String driverClassName;
        private String url;
        private String username;
        private String password;
        // jdbc connection pool
        private int initialSize;
        private int minIdle;
        private int maxActive = 100;
        private long maxWait;
        private long timeBetweenEvictionRunsMillis;
        private long minEvictableIdleTimeMillis;
        private String validationQuery;
        private boolean testWhileIdle;
        private boolean testOnBorrow;
        private boolean testOnReturn;
        private boolean poolPreparedStatements;
        private int maxPoolPreparedStatementPerConnectionSize;
        // filter
        private String filters;
    
        public String getDriverClassName() {
            return driverClassName;
        }
    
        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public int getInitialSize() {
            return initialSize;
        }
    
        public void setInitialSize(int initialSize) {
            this.initialSize = initialSize;
        }
    
        public int getMinIdle() {
            return minIdle;
        }
    
        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }
    
        public int getMaxActive() {
            return maxActive;
        }
    
        public void setMaxActive(int maxActive) {
            this.maxActive = maxActive;
        }
    
        public long getMaxWait() {
            return maxWait;
        }
    
        public void setMaxWait(long maxWait) {
            this.maxWait = maxWait;
        }
    
        public long getTimeBetweenEvictionRunsMillis() {
            return timeBetweenEvictionRunsMillis;
        }
    
        public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
            this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        }
    
        public long getMinEvictableIdleTimeMillis() {
            return minEvictableIdleTimeMillis;
        }
    
        public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
            this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        }
    
        public String getValidationQuery() {
            return validationQuery;
        }
    
        public void setValidationQuery(String validationQuery) {
            this.validationQuery = validationQuery;
        }
    
        public boolean isTestWhileIdle() {
            return testWhileIdle;
        }
    
        public void setTestWhileIdle(boolean testWhileIdle) {
            this.testWhileIdle = testWhileIdle;
        }
    
        public boolean isTestOnBorrow() {
            return testOnBorrow;
        }
    
        public void setTestOnBorrow(boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }
    
        public boolean isTestOnReturn() {
            return testOnReturn;
        }
    
        public void setTestOnReturn(boolean testOnReturn) {
            this.testOnReturn = testOnReturn;
        }
    
        public boolean isPoolPreparedStatements() {
            return poolPreparedStatements;
        }
    
        public void setPoolPreparedStatements(boolean poolPreparedStatements) {
            this.poolPreparedStatements = poolPreparedStatements;
        }
    
        public int getMaxPoolPreparedStatementPerConnectionSize() {
            return maxPoolPreparedStatementPerConnectionSize;
        }
    
        public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
            this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
        }
    
        public String getFilters() {
            return filters;
        }
    
        public void setFilters(String filters) {
            this.filters = filters;
        }
    }
    
  4. 配置Servlet和Filter

    在config包下新建一个DruidConfig配置类,主要用于注入属性和连接池相关配置,如黑白名单、监控管理后台登录账户密码等

    package com.springframe.festmon.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import com.springframe.festmon.properties.DruidDataSourceProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    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.servlet.Filter;
    import javax.servlet.Servlet;
    import javax.sql.DataSource;
    import java.sql.SQLException;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    
    @Configuration
    @EnableConfigurationProperties({DruidDataSourceProperties.class})
    //@EnableConfigurationProperties注解用于导入上一步自定义的Druid的配置信息。
    public class DruidConfig {
    
        @Autowired
        private DruidDataSourceProperties properties;
    
        @Bean
        @ConditionalOnMissingBean
        public DataSource druidDataSource() {
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(properties.getDriverClassName());
            druidDataSource.setUrl(properties.getUrl());
            druidDataSource.setUsername(properties.getUsername());
            druidDataSource.setPassword(properties.getPassword());
            druidDataSource.setInitialSize(properties.getInitialSize());
            druidDataSource.setMinIdle(properties.getMinIdle());
            druidDataSource.setMaxActive(properties.getMaxActive());
            druidDataSource.setMaxWait(properties.getMaxWait());
            druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
            druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
            druidDataSource.setValidationQuery(properties.getValidationQuery());
            druidDataSource.setTestWhileIdle(properties.isTestWhileIdle());
            druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
            druidDataSource.setTestOnReturn(properties.isTestOnReturn());
            druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(properties.getMaxPoolPreparedStatementPerConnectionSize());
            try {
                druidDataSource.setFilters(properties.getFilters());
                druidDataSource.init();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return druidDataSource;
        }
    
        /**
         * 配置 Druid 监控管理后台的Servlet;
         * 内置 Servler 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
         * public ServletRegistrationBean druidServlet()相当于WebServlet配置。
         */
        @Bean
        @ConditionalOnMissingBean
        public ServletRegistrationBean<Servlet> druidServlet(){
            ServletRegistrationBean<Servlet> servletServletRegistrationBean = new ServletRegistrationBean<Servlet>(new StatViewServlet(), "/druid/*");
            //白名单
            servletServletRegistrationBean.addInitParameter("allow","127.0.0.1");  //表示只有本机可以访问,为空或者为null时,表示允许所有访问
            //ip黑名单(存在共同时,deny优先于allow)
            //如果满足deny的话会提示,sorry, you are not permitted to view this page
            servletServletRegistrationBean.addInitParameter("deny","172.13.13.31");
            //登录查看信息的账号和密码,用于登录Druid监控后台
            servletServletRegistrationBean.addInitParameter("loginUsername","admin");
            servletServletRegistrationBean.addInitParameter("loginPassword","admin");
            //是否能重置数据
            servletServletRegistrationBean.addInitParameter("resetEnable","true");
            return servletServletRegistrationBean;
        }
    
        /**
         * 配置 Druid 监控 之  web 监控的 filter
         * WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
         * public FilterRegistrationBean filterRegistrationBean()相当于Web Filter配置。
         */
        @Bean
        @ConditionalOnMissingBean
        public FilterRegistrationBean<Filter> filterFilterRegistrationBean(){
            FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>();
            bean.setFilter(new WebStatFilter());
    
            //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
            Map<String, String> initParams = new HashMap<>();
            initParams.put("exclusions", "*.js,*.css,/druid/*");
            bean.setInitParameters(initParams);
    
            //"/*" 表示过滤所有请求
            bean.setUrlPatterns(Collections.singletonList("/*"));
            return bean;
        }
    }		
    

配置slf4j

​ Spring Boot默认支持slf4j+logback的日志框架,如果想要自定义日志策略,那么在根目录下添加配置文件进行一些配置即可。

  1. 在pom.xml文件中添加依赖

    <!-- @Slf4j注解 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    
  2. 在idea下添加Lombok插件

    Spring Cloud + Vue 电商项目开发实战(2) ——配置Druid及slf4j

  3. 修改application.yaml文件

    logging:
      config: logback.xml
      level:
        com.springframe.festmon.dao: trace
    
  4. 在根目录下创建logback.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- configuration标签属性
        scan        当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
        scanPeriod  设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
        debug       当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
    -->
    <configuration debug="false">
        <!--储存日志文件的地址,使用绝对路径-->
        <property name="LOG_HOME" value="/Users/YYX/Desktop/MyProject/个人日志文件/Spring\ Cloud\ +\ Vue项目/Spring\ Cloud后台Demo/festmon/logs"/>
        <!-- 日志的格式
            %d          表示日期
            %thread     表示线程名
            %-5level    日志级别,从左显示5个字符宽度
            %logger{56} 日志打印所属的类名,限定长度56个字符
            %msg        日志消息
            %n          是换行符
            颜色设置    %颜色(以上参数),例如,%highlight(%-5level)
            支持颜色    "%black", "%red", "%green","%yellow","%blue", "%magenta","%cyan", "%white", "%gray", "%boldRed",
                        "%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" and "%highlight"
        -->
        <property name="LOG_FORMAT" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] [%logger{56}]: %msg%n"/>
        <property name="LOG_COLOR_FORMAT" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%cyan(%thread)] [%highlight(%-5level)] [%green(%logger{56})]: %msg%n"/>
    
        <!-- 控制台输出 -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <!-- 过滤掉 TRACE 级别的日志-->
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>TRACE</level>
            </filter>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <!-- 日志的格式化输出 -->
                <pattern>${LOG_COLOR_FORMAT}</pattern>
            </encoder>
        </appender>
    
        <!-- 每天生成日志文件 -->
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 过滤掉 TRACE 级别的日志-->
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>TRACE</level>
            </filter>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>180</MaxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <!--格式化输出-->
                <pattern>${LOG_FORMAT}</pattern>
            </encoder>
            <!--日志文件最大的大小-->
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <MaxFileSize>10MB</MaxFileSize>
            </triggeringPolicy>
        </appender>
    
        <!-- 日志输出级别 -->
        <root level="INFO">
            <appender-ref ref="STDOUT"/>
            <appender-ref ref="FILE"/>
        </root>
    
        <!-- 根据特殊需求指定局部日志级别 -->
        <logger name="org.springframework.jdbc.datasource.DataSourceTransactionManager" level="DEBUG"/>
    </configuration>
    
  5. 测试

    修改SysUserController文件

    @RestController
    @Slf4j
    public class SysUserController {
    
        @Autowired
        private SysUserService sysUserService;
    
        @ApiOperation(value = "选择全部用户")
        @GetMapping("/user/selectAll")
        public Object selectAllUsers(){
            log.info("======测试日志info级别打印=====");
            log.error("=====测试日志error级别打印====");
            log.warn("======测试日志warn级别打印=====");
            return sysUserService.list();
        }
    }
    

    测试结果如下

    Spring Cloud + Vue 电商项目开发实战(2) ——配置Druid及slf4j

上一篇:Ingress


下一篇:Druid源码阅读5-DruidDataSource的shrink过程