Druid初探

Druid簡介

Druid是阿里巴巴開源平臺上的一個項目,整個項目由數據庫連接池、插件框架和SQL解析器組成。該項目主要是為了擴展JDBC的一些限制,可以讓程序員實現一些特殊的需求,比如向密鑰服務請求憑證、統計SQL信息、SQL性能收集、SQL注入檢查、SQL翻譯等,程序員可以通過定制來實現自己需要的功能。
**Druid是一個JDBC組件,它包括三部分: **

  • DruidDriver 代理Driver,能夠提供基于Filter-Chain模式的插件體系。
  • DruidDataSource 高效可管理的數據庫連接池。
  • SQLParser

**Druid可以做什么? **

  1. 可以監控數據庫訪問性能,Druid內置提供了一個功能強大的StatFilter插件,能夠詳細統計SQL的執行性能,這對于線上分析數據庫訪問性能有幫助。
  2. 替換DBCPC3P0。Druid提供了一個高效、功能強大、可擴展性好的數據庫連接池。
  3. 數據庫密碼加密。直接把數據庫密碼寫在配置文件中,這是不好的行為,容易導致安全問題。DruidDruiver和DruidDataSource都支持PasswordCallback。
  4. SQL執行日志,Druid提供了不同的LogFilter,能夠支持Common-LoggingLog4j和JdkLog,你可以按需要選擇相應的LogFilter,監控你應用的數據庫訪問情況。
    擴展JDBC,如果你要對JDBC層有編程的需求,可以通過Druid提供的Filter-Chain機制,很方便編寫JDBC層的擴展插件。
    如下是一個基于Druid內置擴展StatFilter的監控實現:
StatFilter的監控

Druid的Hello world

創建一個Maven工程,數據庫采用MySQL。
pom文件加入

 <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.0.29</version>
    </dependency>
 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
    </dependency>

配置信息采用properties文件,Druid創建DataSource支持Properties方式和Map方式。properties文件如下:

#基本屬性 url、user、password
url=jdbc:mysql://localhost:3306/istep?useUnicode=true&characterEncoding=utf-8
username=istep
password=istep
#配置初始化大小、最小、最大
initialSize=1
minIdle=1
maxActive=20
#配置獲取連接等待超時的時間
maxWait=60000
#配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
timeBetweenEvictionRunsMillis=60000
#配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 'x'
testWhileIdle=true
testOnBorrow=false
testOnReturn=false

代碼如下:

package com.sima.db;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * Created by cfq on 2017/4/29.
 */
public class DruidTest {

    public static void main(String[] args){
        String confile = "druid.properties";//配置文件名稱
        Properties properties = new Properties();
        InputStream inputStream = null;
        DruidTest druidTest = new DruidTest();
        confile = druidTest.getClass().getResource("/").getPath()+ confile;//獲取配置文件路徑
        System.out.println(confile);

        File file = new File(confile);
        try {
            inputStream = new BufferedInputStream(new FileInputStream(file));
            properties.load(inputStream);//加載配置文件
            
            System.out.println(properties);
            System.out.println("url:" + properties.getProperty("url"));
            System.out.println("username:" + properties.getProperty("username"));
            System.out.println("password:" + properties.getProperty("password"));

            //通過DruidDataSourceFactory獲取javax.sql.DataSource
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

            //以下為一個查詢的例子,和正常的jdbc操作一樣
            String sql = "select * from sys_user";
            PreparedStatement pstmt;
            try {
                pstmt = (PreparedStatement)dataSource.getConnection().prepareStatement(sql);
                ResultSet rs = pstmt.executeQuery();
                int col = rs.getMetaData().getColumnCount();
                System.out.println("============================");
                while (rs.next()) {
                    for (int i = 1; i <= col; i++) {
                        System.out.print(rs.getString(i) + "\t");
                    }
                    System.out.println("");
                }
                System.out.println("============================");
            } catch (SQLException e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

配置文件的參數說明

配置 缺省值 說明
name 配置這個屬性的意義在于,如果存在多個數據源,監控的時候可以通過名字來區分開來。如果沒有配置,將會生成一個名字,格式是:"DataSource-" + System.identityHashCode(this)
url 連接數據庫的url,不同數據庫不一樣。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 連接數據庫的用戶名
password 連接數據庫的密碼。如果你不希望密碼直接寫在配置文件中,可以使用ConfigFilter。詳細看這里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName 根據url自動識別這一項可配可不配,如果不配置druid會根據url自動識別dbType,然后選擇相應的driverClassName
initialSize 0 初始化時建立物理連接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時
maxActive 8 最大連接池數量
maxIdle 8 已經不再使用,配置了也沒效果
minIdle 最小連接池數量
maxWait 獲取連接時最大等待時間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
poolPreparedStatements false 是否緩存preparedStatement,也就是PSCache。PSCache對支持游標的數據庫性能提升巨大,比如說oracle。在mysql下建議關閉。
maxOpenPreparedStatements -1 要啟用PSCache,必須配置大于0,當大于0時,poolPreparedStatements自動觸發修改為true。在Druid中,不會存在Oracle下PSCache占用內存過多的問題,可以把這個數值配置大一些,比如說100
validationQuery 用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用。
testOnBorrow true 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。
testOnReturn false 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能
testWhileIdle false 建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。timeBetweenEvictionRunsMillis有兩個含義:1) Destroy線程會檢測連接的間隔時間 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
numTestsPerEvictionRun 不再使用,一個DruidDataSource只支持一個EvictionRun
minEvictableIdleTimeMillis 配置一個連接在池中最小生存的時間,單位是毫秒
connectionInitSqls 物理連接初始化的時候執行的sql
exceptionSorter 根據dbType自動識別當數據庫拋出一些不可恢復的異常時,拋棄連接
filters 屬性類型是字符串,通過別名的方式配置擴展插件,常用的插件有:監控統計用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
proxyFilters 配置監控統計攔截的filters。類型是List<com.alibaba.druid.filter.Filter>,如果同時配置了filters和proxyFilters,是組合關系,并非替換關系?
timeBetweenEvictionRunsMillis 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒

數據庫密碼加密

數據庫密碼直接寫在配置中,對運維安全來說,是一個很大的挑戰。Druid為此提供一種數據庫密碼加密的手段ConfigFilter。

執行命令加密數據庫密碼

1.在命令行中執行如下命令:

java -cp druid-1.0.16.jar com.alibaba.druid.filter.config.ConfigTools you_password

輸出

privateKey:MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA6+4avFnQKP+O7bu5YnxWoOZjv3no4aFV558HTPDoXs6EGD0HP7RzzhGPOKmpLQ1BbA5viSht+aDdaxXp6SvtMQIDAQABAkAeQt4fBo4SlCTrDUcMANLDtIlax/I87oqsONOg5M2JS0jNSbZuAXDv7/YEGEtMKuIESBZh7pvVG8FV531/fyOZAiEA+POkE+QwVbUfGyeugR6IGvnt4yeOwkC3bUoATScsN98CIQDynBXC8YngDNwZ62QPX+ONpqCel6g8NO9VKC+ETaS87wIhAKRouxZL38PqfqV/WlZ5ZGd0YS9gA360IK8zbOmHEkO/AiEAsES3iuvzQNYXFL3x9Tm2GzT1fkSx9wx+12BbJcVD7AECIQCD3Tv9S+AgRhQoNcuaSDNluVrL/B/wOmJRLqaOVJLQGg==
publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOvuGrxZ0Cj/ju27uWJ8VqDmY7956OGhVeefB0zw6F7OhBg9Bz+0c84RjzipqS0NQWwOb4kobfmg3WsV6ekr7TECAwEAAQ==
password:PNak4Yui0+2Ft6JSoKBsgNPl+A033rdLhFw+L0np1o+HDRrCo9VkCuiiXviEMYwUgpHZUFxb2FpE0YmSguuRww==

輸入你的數據庫密碼,輸出的是加密后的結果。
2.配置數據源,提示Druid數據源需要對數據庫密碼進行解密。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:derby:memory:spring-test;create=true" />
<property name="username" value="sa" />
<property name="password" value="${password}" /> <property name="filters" value="config" />
<property name="connectionProperties" value="config.decrypt=true;config.decrypt.key=${publickey}" /></bean>

3.配置參數,讓ConfigFilter解密密碼
有三種方式配置:
可以在配置文件my.properties中指定config.decrypt=true
也可以在DruidDataSource的ConnectionProperties中指定config.decrypt=true
也可以在jvm啟動參數中指定-Ddruid.config.decrypt=true

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容