Spring Boot 數據庫連接池入門

本文在提供完整代碼示例,可見 https://github.com/YunaiV/SpringBoot-Labs 的 lab-19 目錄。

原創不易,給點個 Star 嘿,一起衝鴨!

1. 概述

在我們的項目中,數據庫連接池基本是必不可少的組件。在目前數據庫連接池的選型中,主要是

  • Druid ,爲監控而生的數據庫連接池。

  • HikariCP ,號稱性能最好的數據庫連接池。

至於怎麼選擇,兩者都非常優秀,不用過多糾結。

  • Spring Boot 2.X 版本,默認採用 HikariCP 。

  • 阿里大規模採用 Druid 。

當然,如下有一些資料,胖友可以閱讀參考:

  • 《Druid 連接池介紹》

  • 《爲什麼 HikariCP 被號稱爲性能最好的 Java 數據庫連接池,如何配置使用》

  • 《alibaba/druid pool analysis》 ,一個小小的“撕逼”。

下面,我們來進行 HikariCP 和 Druid 的入門,會配置單數據源和多數據源情況下的連接池。

2. HikariCP 單數據源

示例代碼對應倉庫:lab-19-datasource-pool-hikaricp-single 。

在本小節,我們會使用配置一個數據源的 HikariCP 連接池。

艿艿:推薦入門後,可以看看 HikariCP 的文檔:https://github.com/brettwooldridge/HikariCP/wiki 。

2.1 引入依賴

在 pom.xml 文件中,引入相關依賴。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-19-datasource-pool-hikaricp-single</artifactId>

    <dependencies>
        <!-- 實現對數據庫連接池的自動化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency> <!-- 本示例,我們使用 MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>

        <!-- 方便等會寫單元測試 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
  • 無需主動引入 HikariCP 的依賴。因爲在 Spring Boot 2.X 中,spring-boot-starter-jdbc 默認引入 com.zaxxer.HikariCP 依賴。

2.2 應用配置文件

在 application.yml 中,添加 HikariCP 配置,如下:

spring:
  # datasource 數據源配置內容,對應 DataSourceProperties 配置屬性類
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root # 數據庫賬號
    password: # 數據庫密碼
    # HikariCP 自定義配置,對應 HikariConfig 配置屬性類
    hikari:
      minimum-idle: 10 # 池中維護的最小空閒連接數,默認爲 10 個。
      maximum-pool-size: 10 # 池中最大連接數,包括閒置和使用中的連接,默認爲 10 個。
  • 在 spring.datasource 配置項下,我們可以添加數據源的通用配置。

  • 在 spring.datasource.hikari 配置項下,我們可以添加 HikariCP 連接池的自定義配置。然後 DataSourceConfiguration.Hikari 會自動化配置 HikariCP 連接池。

HikariCP 更多配置項,可以看看如下表格:s

FROM 《HikariCP 連接池及其在 Spring Boot 中的配置》

配置項 描述 構造器默認值 默認配置validate之後的值 validate重置
autoCommit 自動提交從池中返回的連接 true true -
connectionTimeout 等待來自池的連接的最大毫秒數 SECONDS.toMillis(30) = 30000 30000 如果小於250毫秒,則被重置回30秒
idleTimeout 連接允許在池中閒置的最長時間 MINUTES.toMillis(10) = 600000 600000 如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,則會被重置爲0(代表永遠不會退出);如果idleTimeout!=0且小於10秒,則會被重置爲10秒
maxLifetime 池中連接最長生命週期 MINUTES.toMillis(30) = 1800000 1800000 如果不等於0且小於30秒則會被重置回30分鐘
connectionTestQuery 如果您的驅動程序支持JDBC4,我們強烈建議您不要設置此屬性 null null -
minimumIdle 池中維護的最小空閒連接數 -1 10 minIdle<0或者minIdle>maxPoolSize,則被重置爲maxPoolSize
maximumPoolSize 池中最大連接數,包括閒置和使用中的連接 -1 10 如果maxPoolSize小於1,則會被重置。當minIdle<=0被重置爲DEFAULT_POOL_SIZE則爲10;如果minIdle>0則重置爲minIdle的值
metricRegistry 該屬性允許您指定一個 Codahale / Dropwizard MetricRegistry 的實例,供池使用以記錄各種指標 null null -
healthCheckRegistry 該屬性允許您指定池使用的Codahale / Dropwizard HealthCheckRegistry的實例來報告當前健康信息 null null -
poolName 連接池的用戶定義名稱,主要出現在日誌記錄和JMX管理控制檯中以識別池和池配置 null HikariPool-1 -
initializationFailTimeout 如果池無法成功初始化連接,則此屬性控制池是否將 fail fast 1 1 -
isolateInternalQueries 是否在其自己的事務中隔離內部池查詢,例如連接活動測試 false false -
allowPoolSuspension 控制池是否可以通過JMX暫停和恢復 false false -
readOnly 從池中獲取的連接是否默認處於只讀模式 false false -
registerMbeans 是否註冊JMX管理Bean(MBeans false false -
catalog 爲支持 catalog 概念的數據庫設置默認 catalog driver default null -
connectionInitSql 該屬性設置一個SQL語句,在將每個新連接創建後,將其添加到池中之前執行該語句。 null null -
driverClassName HikariCP將嘗試通過僅基於jdbcUrl的DriverManager解析驅動程序,但對於一些較舊的驅動程序,還必須指定driverClassName null null -
transactionIsolation 控制從池返回的連接的默認事務隔離級別 null null -
validationTimeout 連接將被測試活動的最大時間量 SECONDS.toMillis(5) = 5000 5000 如果小於250毫秒,則會被重置回5秒
leakDetectionThreshold 記錄消息之前連接可能離開池的時間量,表示可能的連接泄漏 0 0 如果大於0且不是單元測試,則進一步判斷:(leakDetectionThreshold < SECONDS.toMillis(2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0),會被重置爲0 . 即如果要生效則必須>0,而且不能小於2秒,而且當maxLifetime > 0時不能大於maxLifetime
dataSource 這個屬性允許你直接設置數據源的實例被池包裝,而不是讓HikariCP通過反射來構造它 null null -
schema 該屬性爲支持模式概念的數據庫設置默認模式 driver default null -
threadFactory 此屬性允許您設置將用於創建池使用的所有線程的java.util.concurrent.ThreadFactory的實例。 null null -
scheduledExecutor 此屬性允許您設置將用於各種內部計劃任務的java.util.concurrent.ScheduledExecutorService實例 null null -

2.3 Application

創建 Application.java 類,配置 @SpringBootApplication 註解即可。代碼如下:

// Application.java

@SpringBootApplication
public class Application implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    private DataSource dataSource;

    public static void main(String[] args) {
        // 啓動 Spring Boot 應用
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        try (Connection conn = dataSource.getConnection()) {
            // 這裏,可以做點什麼
            logger.info("[run][獲得連接:{}]", conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

通過實現 CommandLineRunner 接口,應用啓動完成後,回調 #run(String... args) 方法,輸出下 Connection 信息。執行日誌如下:

2019-11-12 11:15:32.303  INFO 41198 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-11-12 11:15:32.472  INFO 41198 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-11-12 11:15:32.473  INFO 41198 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][獲得連接:HikariProxyConnection@1561745898 wrapping com.mysql.jdbc.JDBC4Connection@793138bd]
  • 可以看到,HikariDataSource 成功初始化。

3. HikariCP 多數據源

示例代碼對應倉庫:lab-19-datasource-pool-hikaricp-multiple 。

在本小節,我們會使用配置兩個數據源的 HikariCP 連接池。

3.1 引入依賴

和 「2.1 引入依賴」 是一致。

3.2 應用配置文件

在 application.yml 中,添加 HikariCP 配置,如下:

spring:
  # datasource 數據源配置內容
  datasource:
    # 訂單數據源配置
    orders:
      url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password:
      # HikariCP 自定義配置,對應 HikariConfig 配置屬性類
      hikari:
        minimum-idle: 20 # 池中維護的最小空閒連接數,默認爲 10 個。
        maximum-pool-size: 20 # 池中最大連接數,包括閒置和使用中的連接,默認爲 10 個。
    # 用戶數據源配置
    users:
      url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password:
      # HikariCP 自定義配置,對應 HikariConfig 配置屬性類
      hikari:
        minimum-idle: 15 # 池中維護的最小空閒連接數,默認爲 10 個。
        maximum-pool-size: 15 # 池中最大連接數,包括閒置和使用中的連接,默認爲 10 個。
  • 我們在 spring.datasource 配置項下,定義了 orders 和 users 兩個數據源的配置。而每個數據源的配置,和我們在 「2.2 應用配置文件」 是一致的。

3.3 數據源配置類

3.3.1 錯誤的示例

在網上,我們會看到這樣配置多個數據源的配置類。代碼如下:

@Bean(name = "ordersDataSource")
@ConfigurationProperties(prefix = "spring.datasource.orders")
public DataSource ordersDataSource() {
   return DataSourceBuilder.create().build();
}

@Bean(name = "usersDataSource")
@ConfigurationProperties(prefix = "spring.datasource.users")
public DataSource ordersDataSource() {
   return DataSourceBuilder.create().build();
}
  • 實際上,這樣做的話,在部分場景下,會存在問題,這是爲什麼呢?

  • 我們先來理解這段程序的用途。以 #ordersDataSource() 方法爲例子:

    • DataSourceBuilder.create().build() 代碼段,會創建一個 DataSource 數據源。

    • 搭配上 @Bean(name = "ordersDataSource") 註解,會創建一個名字爲 "ordersDataSource" 的 DataSource Bean 。這裏,我們使用 HikariCP ,所以返回的是 HikariDataSource Bean 。

    • @ConfigurationProperties(prefix = "spring.datasource.orders") 註解,會將 "spring.datasource.orders" 配置項,逐個屬性賦值給 DataSource Bean 。

  • 看起來貌似沒問題,但是如果每個數據源如果有 HikariCP 的 "hikari" 自定義配置項時,它的自定義配置項無法設置到 HikariDataSource Bean 中。因爲,"spring.datasource.orders.hikari" 是 "spring.datasource.orders" 的第二層屬性。而 HikariDataSource 的配置屬性在第一層,這就導致無法正確的設置。

雖然存在該問題,但是大多數項目,我們並不會自定義 HikariCP 的 "hikari" 配置項,所以這個問題就偷偷藏起來,**“不存在”**了。

3.3.2 正確的示例

當然,作爲入門的示例,艿艿還是希望提供正確的做法。

在 cn.iocoder.springboot.lab19.datasourcepool.config 包路徑下,我們會創建 DataSourceConfig 配置類。代碼如下:

// DataSourceConfig.java

@Configuration
public class DataSourceConfig {

    /**
     * 創建 orders 數據源的配置對象
     */
    @Primary
    @Bean(name = "ordersDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.orders") // 讀取 spring.datasource.orders 配置到 DataSourceProperties 對象
    public DataSourceProperties ordersDataSourceProperties() {
        return new DataSourceProperties();
    }

    /**
     * 創建 orders 數據源
     */
    @Bean(name = "ordersDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.orders.hikari") // 讀取 spring.datasource.orders 配置到 HikariDataSource 對象
    public DataSource ordersDataSource() {
        // <1.1> 獲得 DataSourceProperties 對象
        DataSourceProperties properties =  this.ordersDataSourceProperties();
        // <1.2> 創建 HikariDataSource 對象
        return createHikariDataSource(properties);
    }

    /**
     * 創建 users 數據源的配置對象
     */
    @Bean(name = "usersDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.users") // 讀取 spring.datasource.users 配置到 DataSourceProperties 對象
    public DataSourceProperties usersDataSourceProperties() {
        return new DataSourceProperties();
    }

    /**
     * 創建 users 數據源
     */
    @Bean(name = "usersDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.users.hikari")
    public DataSource usersDataSource() {
        // 獲得 DataSourceProperties 對象
        DataSourceProperties properties =  this.usersDataSourceProperties();
        // 創建 HikariDataSource 對象
        return createHikariDataSource(properties);
    }

    private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {
        // 創建 HikariDataSource 對象
        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        // 設置線程池名
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        return dataSource;
    }

}
  • 這塊代碼,我們是參考 Spring Boot DataSourceConfiguration.Hikari 配置類來實現的。

  • #ordersDataSourceProperties() 方法,創建 "orders" 數據源的 DataSourceProperties 配置對象。

    • @Primary 註解,保證項目中有一個的 DataSourceProperties Bean 。

    • new DataSourceProperties() 代碼段,會創建一個 DataSourceProperties 數據源的配置對象。

    • 搭配上 @Bean(name = "ordersDataSourceProperties") 註解,會創建一個名字爲 "ordersDataSourceProperties" 的 DataSourceProperties Bean 。

    • @ConfigurationProperties(prefix = "spring.datasource.orders") 註解,會將 "spring.datasource.orders" 配置項,逐個屬性賦值給 DataSourceProperties Bean 。

  • #ordersDataSource() 方法,創建 orders 數據源。

    • <1.1> 處,調用 #ordersDataSourceProperties() 方法,獲得 orders 數據源的 DataSourceProperties 。

    • <1.2> 處,調用 #createHikariDataSource(DataSourceProperties properties) 方法,創建 HikariDataSource 對象。這樣,"spring.datasource.orders" 配置項,逐個屬性賦值給 HikariDataSource Bean 。

    • 搭配上 @Bean(name = "ordersDataSource") 註解,會創建一個名字爲 "ordersDataSource" 的 HikariDataSource Bean 。

    • @ConfigurationProperties(prefix = "spring.datasource.orders.hikari") 註解,會將 HikariCP 的 "spring.datasource.orders.hikari" 自定義配置項,逐個屬性賦值給 HikariDataSource Bean 。

  • users 數據源的配置,同上,就不重複解釋了。

3.4 Application

創建 Application.java 類,配置 @SpringBootApplication 註解即可。代碼如下:

// Application.java

@SpringBootApplication
public class Application implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(Application.class);

    @Resource(name = "ordersDataSource")
    private DataSource ordersDataSource;

    @Resource(name = "usersDataSource")
    private DataSource usersDataSource;

    public static void main(String[] args) {
        // 啓動 Spring Boot 應用
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        // orders 數據源
        try (Connection conn = ordersDataSource.getConnection()) {
            // 這裏,可以做點什麼
            logger.info("[run][ordersDataSource 獲得連接:{}]", conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        // users 數據源
        try (Connection conn = usersDataSource.getConnection()) {
            // 這裏,可以做點什麼
            logger.info("[run][usersDataSource 獲得連接:{}]", conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}

執行日誌如下:

2019-11-12 15:30:35.060  INFO 45868 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-11-12 15:30:35.365  INFO 45868 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-11-12 15:30:35.367  INFO 45868 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][ordersDataSource 獲得連接:HikariProxyConnection@1041547629 wrapping com.mysql.jdbc.JDBC4Connection@3c989952]
2019-11-12 15:30:35.371  INFO 45868 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Starting...
2019-11-12 15:30:35.376  INFO 45868 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Start completed.
2019-11-12 15:30:35.376  INFO 45868 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][usersDataSource 獲得連接:HikariProxyConnection@795748540 wrapping com.mysql.jdbc.JDBC4Connection@7c098bb3]
  • 可以看到,兩個 HikariDataSource 成功初始化。

多數據源和 JPA、MyBatis、JdbcTemplate 的集成,可以看看 《芋道 Spring Boot 多數據源(讀寫分離)入門》 文章。

4. Druid 單數據源

示例代碼對應倉庫:lab-19-datasource-pool-druid-single 。

在本小節,我們會使用配置一個數據源的 Druid 連接池。並簡單看看 Druid 的監控功能。

艿艿:推薦入門後,可以看看 Druid 的文檔:https://github.com/alibaba/druid/wiki/ 。

4.1 引入依賴

在 pom.xml 文件中,引入相關依賴。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-19-datasource-pool-druid-single</artifactId>

    <dependencies>
        <!-- 保證 Spring JDBC 的依賴健全 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- 實現對 Druid 連接池的自動化配置 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
        <dependency> <!-- 本示例,我們使用 MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>

        <!-- 實現對 Spring MVC 的自動化配置,因爲我們需要看看 Druid 的監控功能 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 方便等會寫單元測試 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

具體每個依賴的作用,胖友自己認真看下艿艿添加的所有註釋噢。S

4.2 應用配置文件

在 application.yml 中,添加 Druid 配置,如下:

spring:
  # datasource 數據源配置內容,對應 DataSourceProperties 配置屬性類
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root # 數據庫賬號
    password: # 數據庫密碼
    type: com.alibaba.druid.pool.DruidDataSource # 設置類型爲 DruidDataSource
    # Druid 自定義配置,對應 DruidDataSource 中的 setting 方法的屬性
    druid:
      min-idle: 0 # 池中維護的最小空閒連接數,默認爲 0 個。
      max-active: 20 # 池中最大連接數,包括閒置和使用中的連接,默認爲 8 個。
      filter:
        stat: # 配置 StatFilter ,對應文檔 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter
          log-slow-sql: true # 開啓慢查詢記錄
          slow-sql-millis: 5000 # 慢 SQL 的標準,單位:毫秒
      stat-view-servlet: # 配置 StatViewServlet ,對應文檔 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE
        enabled: true # 是否開啓 StatViewServlet
        login-username: yudaoyuanma # 賬號
        login-password: javaniubi # 密碼
  • spring.datasource 配置項,設置 Spring 數據源的通用配置。其中,spring.datasource.type 配置項,需要主動設置使用 DruidDataSource 。因爲默認情況下,spring-boot-starter-jdbc 的 DataSourceBuilder 會按照 DATA_SOURCE_TYPE_NAMES 的順序,嘗試推斷數據源的類型。

  • spring.datasource.druid 配置項,設置 Druid 連接池的自定義配置。然後 DruidDataSourceAutoConfigure 會自動化配置 Druid 連接池。

    • 在 《Druid —— 配置屬性》 和 《DruidDataSource 配置屬性列表》 下,提供了各種 Druid 的配置項,胖友可以自己看看。

    • filter.stat 配置項,我們配置了 Druid StatFilter ,用於統計監控信息。對應文檔 《Druid —— 配置_StatFilter》 。要注意,StatFilter 並不是我們說的 javax.servlet.Filter ,而是 Druid 提供的 Filter 拓展機制。

    • filter.stat-view-servlet 配置項,我們配置了 Druid StatViewServlet ,用於提供監控信息的展示的 html 頁面和 JSON API 。對應文檔 《Druid —— 配置_StatViewServlet 配置》 。StatViewServlet 就是我們說的 javax.servlet.Filter 。

4.3 Application

創建 Application.java 類,配置 @SpringBootApplication 註解即可。代碼如下:

@SpringBootApplication
public class Application implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    private DataSource dataSource;

    public static void main(String[] args) {
        // 啓動 Spring Boot 應用
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        logger.info("[run][獲得數據源:{}]", dataSource.getClass());
    }

}

執行日誌如下:

2019-11-12 19:34:12.079  INFO 48359 --- [           main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2019-11-12 19:34:12.156  INFO 48359 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited

2019-11-12 19:34:12.560  INFO 48359 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][獲得數據源:class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper]
  • 可以看到,DruidDataSource 成功初始化。

4.4 監控功能

因爲我們在 「4.2 應用配置中」 中,做了如下操作:

  • 通過 spring.datasource.filter.stat 配置了 StatFilter ,統計監控信息。

  • 通過 spring.datasource.filter.stat-view-servlet 配置了 StatViewServlet ,提供監控信息的展示的 html 頁面和 JSON API 。

所以我們在啓動項目後,訪問 http://127.0.0.1:8080/druid 地址,可以看到監控 html 頁面。如下圖所示:

  • 在界面的頂部,提供了數據源、SQL 監控、SQL 防火牆等等功能。

  • 每個界面上,可以通過 View JSON API 獲得數據的來源。同時,我們可以在 JSON API( http://127.0.0.1:8080/druid/api.html ) 菜單對應的界面中,看到 StatViewServlet 內置的監控信息的 JSON API 列表。

  • 因爲監控信息是存儲在 JVM 內存中,在 JVM 進程重啓時,信息將會丟失。如果我們希望持久化到 MySQL、Elasticsearch、HBase 等存儲器中,可以通過 StatViewServlet 提供的 JSON API 接口,採集監控信息。另外,有個 druid-aggregated-monitor 開源項目,提供了 集中監控分佈式服務中的 Druid 連接池的方案和思路。

  • 如果 StatViewServlet 提供的 JSON API 接口,無法滿足我們的訴求,我們可以通過自定義 API 接口,使用 DruidStatManagerFacade 獲得監控信息。使用示例 DruidStatController 代碼如下:

    // DruidStatController.java
    
    @RestController
    public class DruidStatController {
    
        @GetMapping("/monitor/druid/stat")
        @Deprecated
        public Object druidStat(){
            // `DruidStatManagerFacade#getDataSourceStatDataList()` 方法,可以獲取所有數據源的監控數據。
            // 除此之外,DruidStatManagerFacade 還提供了一些其他方法,你可以按需選擇使用。
            return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
        }
    
    }
    
    • 當然,絕大多數情況下,我們並不需要做這方面的拓展。

5. Druid 多數據源

示例代碼對應倉庫:lab-19-datasource-pool-druid-multiple 。

在本小節,我們會使用配置兩個數據源的 Druid 連接池。

5.1 引入依賴

和 「4.1 引入依賴」 是一致。

5.2 應用配置

在 application.yml 中,添加 Druid 配置,如下:

spring:
  # datasource 數據源配置內容
  datasource:
    # 訂單數據源配置
    orders:
      url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password:
      type: com.alibaba.druid.pool.DruidDataSource # 設置類型爲 DruidDataSource
      # Druid 自定義配置,對應 DruidDataSource 中的 setting 方法的屬性
      min-idle: 0 # 池中維護的最小空閒連接數,默認爲 0 個。
      max-active: 20 # 池中最大連接數,包括閒置和使用中的連接,默認爲 8 個。
    # 用戶數據源配置
    users:
      url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password:
      type: com.alibaba.druid.pool.DruidDataSource # 設置類型爲 DruidDataSource
      # Druid 自定義配置,對應 DruidDataSource 中的 setting 方法的屬性
      min-idle: 0 # 池中維護的最小空閒連接數,默認爲 0 個。
      max-active: 20 # 池中最大連接數,包括閒置和使用中的連接,默認爲 8 個。
    # Druid 自定已配置
    druid:
      # 過濾器配置
      filter:
        stat: # 配置 StatFilter ,對應文檔 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter
          log-slow-sql: true # 開啓慢查詢記錄
          slow-sql-millis: 5000 # 慢 SQL 的標準,單位:毫秒
      # StatViewServlet 配置
      stat-view-servlet: # 配置 StatViewServlet ,對應文檔 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE
        enabled: true # 是否開啓 StatViewServlet
        login-username: yudaoyuanma # 賬號
        login-password: javaniubi # 密碼
  • 不同於我們在 「3.2 應用配置文件」 中,我們將 Druid 的自定義配置,和 urldriver-class-name 等數據源的通用配置放在同一級,這樣後續我們只需要使用 @ConfigurationProperties(prefix = "spring.datasource.orders") 的方式,就能完成 DruidDataSource 的配置屬性設置。

  • 在 spring.datasource.druid 配置項下,我們還是配置了 filter.stat 和 stat-view-servlet 配置項,用於 Druid 監控功能。

5.3 數據源配置類

在 cn.iocoder.springboot.lab19.datasourcepool.config 包路徑下,我們會創建 DataSourceConfig 配置類。代碼如下:

// DataSourceConfig.java

@Configuration
public class DataSourceConfig {

    /**
     * 創建 orders 數據源
     */
    @Primary
    @Bean(name = "ordersDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.orders") // 讀取 spring.datasource.orders 配置到 HikariDataSource 對象
    public DataSource ordersDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 創建 users 數據源
     */
    @Bean(name = "usersDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.users")
    public DataSource usersDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

}
  • 因爲我們在 「5.2 應用配置」 中,將 Druid 自定義的配置項,和數據源的通用配置放在了同一級,所以我們只需使用 @ConfigurationProperties(prefix = "spring.datasource.orders") 這樣的方式即可。

  • 當然,「3.3.2 正確的示例」 也是可以這麼做的。實際情況下,比較推薦本小節的方式。

5.4 Application

創建 Application.java 類,配置 @SpringBootApplication 註解即可。代碼如下:

// Application.java

@SpringBootApplication
public class Application implements CommandLineRunner {

    private Logger logger = LoggerFactory.getLogger(Application.class);

    @Resource(name = "ordersDataSource")
    private DataSource ordersDataSource;

    @Resource(name = "usersDataSource")
    private DataSource usersDataSource;

    public static void main(String[] args) {
        // 啓動 Spring Boot 應用
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        // orders 數據源
        logger.info("[run][獲得數據源:{}]", ordersDataSource.getClass());

        // users 數據源
        logger.info("[run][獲得數據源:{}]", usersDataSource.getClass());
    }

}

執行日誌如下:

2019-11-12 21:39:24.063  INFO 49670 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][獲得數據源:class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper]
2019-11-12 21:39:24.064  INFO 49670 --- [           main] c.i.s.lab19.datasourcepool.Application   : [run][獲得數據源:class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper]
  • 可以看到,兩個 DruidDataSource 成功初始化。

5.5 監控功能

和 「4.4 監控功能」 一致。

不過呢,我們在監控頁面上,可以看到兩個 Druid 數據庫連接池。

666. 彩蛋

艿艿:咳咳咳,瞎嗶嗶了一些內容,可以選擇不看。😜

艿艿在星球裏,做了一波目前在使用的連接池的調查,大概比例是 Druid : HikariCP 爲 2:1 左右。猜測隨着 Spring Boot 2.X 逐步普及之後,HikariCP 有一定機率反超 Druid 。

雖然說,HikariCP 沒有直接提供監控功能,但是可以使用 Prometheus 採集 Spring Boot Metrics 的數據,後續使用 Grafana 製作儀表盤。目前,已經有 Spring Boot HikariCP / JDBC 可以直接使用。具體怎麼做,胖友可以看看 《Spring Boot 中使用 HikariCP 連接池》 文章。

Druid 的 Issues 3047 中,也有人提出,是否能夠提供 Druid 接入 Prometheus 統一監控的訴求。Druid 目前暫時不支持,不過有聰慧的胖友,提出了使用 Prometheus jmx_exporter 的方式,將 Druid 實現的 JMX 格式的指標暴露出來,提供給 Prometheus 採集監控信息。

在編寫本文的過程中,無意中看到 Druid 文檔中提到,曾經想試驗性的提供 分庫分表 的功能,而艿艿的記憶中,Sharding-JDBC 曾經也想開發數據庫連接池的功能。大體在 DataSource 數據源上做拓展的中間件,可能都不甘於僅僅只覆蓋一塊需求,而是希望成爲一站式的數據庫中間件。立個 Flag ,ShardingSphere 可能會提供數據庫連接池的組件。

旁白君:Sharding-JDBC 是 ShardingSphere 在 JDBC 層面提供的分庫分表組件。當然,不僅僅提供分庫分表的功能,也提供讀寫分離、數據脫敏、分佈式事務等等功能。

如果胖友工作的比較早,一定還接觸過其它連接池。例如說,c3p0、dbcp、BoneCP 等等。數據庫連接池的發展過程,是個非常有意思的歷史。感興趣的胖友,可以看看 《大話數據庫連接池簡史,你都用過幾個?》 一文,江湖味十足~

可能胖友會比較糾結,是否要去自定義連接池的配置呢?一般情況下,默認的配置基本能夠滿足項目的基本要求,不需要特別刻意去修改。當然,這裏推薦看兩篇文章:

  • 《DruidDataSource 配置》 ,Druid 官方提供了通用的配置。

  • 《Druid 連接池推薦配置》 ,某公司的內部實踐。

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