关于druid:技术分享-探究-Druid-连接池探活方式的实现

46次阅读

共计 7754 个字符,预计需要花费 20 分钟才能阅读完成。

作者:刘开洋

爱可生交付服务团队北京 DBA,对数据库及周边技术有浓重的学习趣味,喜爱看书,谋求技术。

本文起源:原创投稿

* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。


最近有钻研到 Druid 的问题,在进行探活检测时无奈失常输入 select x,解决问题后就跑来跟大家分享下 Druid 探活机制的实现。

一、Druid 是什么?Druid 对连贯的探活又是怎么实现的呢?

Druid 是阿里巴巴开源的一款 JDBC 组件,是一款数据库连接池,对 MySQL 的适配性和性能很弱小,包含监控数据库拜访性能、数据库明码加密、SQL 执行日志、以及拓展监控的实现等等,利用到 MySQL 还是很香的。

通过对官网源码 (详见参考) 进行一些简略剖析理解到,应用 Druid 在对连贯进行探活时,波及到以下两个参数的调整:

| 参数 | 阐明 |
| :——— | :–: |
|druid.validationQuery = select 1 | 用来检测连贯是否无效的 sql,要求是一个查问语句,罕用 select ‘x’。如果 validationQuery 为 null,testOnBorrow、testOnReturn、testWhileIdle 都不会起作用。|
| druid.mysql.usePingMethod = false | 敞开 mysql com_ping 探活机制,需启用 validationQuery = select x,开启 validationQuery 探活机制 |

对其余参数的阐明参考配置属性列表:https://github.com/alibaba/dr…

在源码中失去的信息是 Druid 顺次初始化加载 initValidConnectionChecker(); 和 validationQueryCheck(); 在 ValidConnectionChecker 中 默认 com_ping 是开启的,就选用了 com_ping 作为默认探活,上面咱们来别离观测一下 com_ping 和 validationquery 的输入。

咱们接着往下读看看这两个参数的性能具体是怎么实现的。

二、验证

在测试中来敲定这两种探活机制参数的作用吧。这里应用的版本是 Druid 1.2.5

1、com_ping 形式须要通过抓包形式验证

通过 tcpdump 抓包失去在 Druid 连贯中网络包的传输,之后应用 wireshark 进行剖析查看 Druid 发送到 MySQL 的 Request 包。

在 MySQL Protocol 的 Request Command Ping 中失去 Ping (14)。

2、validationquery 形式通过应用 MySQL general log 来验证

public MySqlValidConnectionChecker(){
    try {clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
        if (clazz == null) {clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
        }
   
        if (clazz != null) {ping = clazz.getMethod("pingInternal", boolean.class, int.class);
        }
   
        if (ping != null) {usePingMethod = true;}
    } catch (Exception e) {LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method.  Will use'SELECT 1'instead.", e);
    }
    // 留神这里是从零碎变量中获取的 System.getProperties()。configFromProperties(System.getProperties());
}
   
@Override
public void configFromProperties(Properties properties) {
   // 从零碎变量中获取的,所以应该是在我的项目的启动脚本中增加 usePingMethod=false
   String property = properties.getProperty("druid.mysql.usePingMethod");
   if ("true".equals(property)) {setUsePingMethod(true);
   } else if ("false".equals(property)) {setUsePingMethod(false);
   }
}
    
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {if (conn.isClosed()) {return false;}
   
  if (usePingMethod) {if (conn instanceof DruidPooledConnection) {conn = ((DruidPooledConnection) conn).getConnection();}
   
      if (conn instanceof ConnectionProxy) {conn = ((ConnectionProxy) conn).getRawObject();}
   
       // 以后的 conn 是否是 com.mysql.jdbc.MySQLConnection(or com.mysql.cj.jdbc.ConnectionImpl)
      if (clazz.isAssignableFrom(conn.getClass())) {if (validationQueryTimeout <= 0) {validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;}
   
          try {
              // 应用反射调用 MySQLConnection.pingInternal 办法,查看连贯有效性,并且会刷新连贯的闲暇工夫,如果失败则会抛出异样,下层捕捉
              ping.invoke(conn, true, validationQueryTimeout * 1000);
          } catch (InvocationTargetException e) {Throwable cause = e.getCause();
              if (cause instanceof SQLException) {throw (SQLException) cause;
              }
              throw e;
          }
          return true;
      }
  }
   
  String query = validateQuery;
  // 当 usePingMethod=false 或者 conn 不是 com.mysql.jdbc.MySQLConnection(or com.mysql.cj.jdbc.ConnectionImpl)会执行一下办法
  if (validateQuery == null || validateQuery.isEmpty()) {query = DEFAULT_VALIDATION_QUERY;}
   
  Statement stmt = null;
  ResultSet rs = null;
  try {stmt = conn.createStatement();
      if (validationQueryTimeout > 0) {stmt.setQueryTimeout(validationQueryTimeout);
      }
      // 执行 select x 的 query,并且会刷新连贯的闲暇工夫
      //  如果失败则会抛出异样,下层捕捉
      rs = stmt.executeQuery(query);
      return true;
  } finally {JdbcUtils.close(rs);
      JdbcUtils.close(stmt);
  }
}

druid.validationQuery = SELECT 1 启用无奈间接应用 validationquery,须要通过配置敞开 com_ping(druid.mysql.usePingMethod = false)来实现。这个参数能够间接加在配置文件中,然而应用须要留神一点,如果配置敞开 com_ping 也无奈应用 validationquery 进行探活查问,则可能是程序自身的问题。

程序代码可能存在参数值只拉取 configFromPropety 的参数信息导致 (druid.mysql.usePingMethod = false) 参数生效,以下是我程序修改后的连贯示意图:

// 原程序
public DruidDriverTest() {logger = Logger.getLogger("druid_driver_test");
   this.dataSource = new DruidDataSource();
 
   // Druid 配置文件地址.
   this.configPath = "./config.properties";
···
 
#############################################
// 批改后
public DruidDriverTest() {logger = Logger.getLogger("druid_driver_test");
 
    // Druid 配置文件地址.
    this.configPath = "config.properties";
 
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader(configPath))) {
            // 将配置文件读入到 system.config 中
            System.getProperties().load(bufferedReader);
    } catch (IOException e) {e.printStackTrace();
            return;
    }
···

原程序中:Druid 默认从 config 配置文件 中拉配置参数信息到 DruidDataSource 中,而 usePingMethod 参数须要应用 MySqlValidConnectionChecker 插件加载读取到 DruidDataSource 中,然而 config 没有加载到 System. getProperties()中,因而 Druid 不能辨认 config 配置文件 中的 usePingMethod 参数。Druid 加载 DruidDataSource 中的配置信息进行一系列行为。

批改后:建设 config 配置文件加载到 system 变量中的连贯,再应用 MySqlValidConnectionChecker 插件 加载到 DruidDataSource 中。

[root@yang-02 druid_demo-master]# mvn exec:java -Dexec.mainClass="test.App"
[INFO] Scanning for projects...                                                                
[INFO] ------------------------------------------------------------------------
[INFO] Building druid-demo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] --- exec-maven-plugin:3.0.0:java (default-cli) @ druid-demo ---
[2021-04-28 17:23:06] [SEVERE] minEvictableIdleTimeMillis should be greater than 30000
[2021-04-28 17:23:06] [SEVERE] keepAliveBetweenTimeMillis should be greater than 30000
[2021-04-28 17:23:06] [INFO] start test
[2021-04-28 17:23:06] [INFO] ------------------ status --------------------
[2021-04-28 17:23:06] [INFO] initial size: 3
[2021-04-28 17:23:06] [INFO] min idle: 2
[2021-04-28 17:23:06] [INFO] max active: 20
[2021-04-28 17:23:06] [INFO] current active: 0
[2021-04-28 17:23:06] [INFO] max wait: 6000
[2021-04-28 17:23:06] [INFO] time between eviction runs millis: 2000
[2021-04-28 17:23:06] [INFO] validation query: SELECT 1
[2021-04-28 17:23:06] [INFO] keepAlive: true
[2021-04-28 17:23:06] [INFO] testWhileIdle: false
[2021-04-28 17:23:06] [INFO] testOnBorrow: false
[2021-04-28 17:23:06] [INFO] testOnReturn: false
[2021-04-28 17:23:06] [INFO] keepAliveBetweenTimeMillis: 4000
[2021-04-28 17:23:06] [INFO] MinEvictableIdleTimeMillis: 2000
[2021-04-28 17:23:06] [INFO] MaxEvictableIdleTimeMillis: 25200000
[2021-04-28 17:23:06] [INFO] RemoveAbandoned: false
[2021-04-28 17:23:06] [INFO] RemoveAbandonedTimeoutMillis: 300000
[2021-04-28 17:23:06] [INFO] RemoveAbandonedTimeout: 300
[2021-04-28 17:23:06] [INFO] LogAbandoned: false
 
  
// 通过开启 MySQL general log 观测 Druid 下发查问的命令输入
// mysql general log output
2021-04-28T17:23:01.435944+08:00     7048 Connect   root@127.0.0.1 on druid_demo using TCP/IP
2021-04-28T17:23:01.441663+08:00     7048 Query /* mysql-connector-java-5.1.40 (Revision: 402933ef52cad9aa82624e80acbea46e3a701ce6)
*/SELECT  @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client,
@@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results,
@@character_set_server AS character_set_server,@@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout,
@@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet,
@@net_buffer_length AS net_buffer_length, @@net_write_timeout AS net_write_timeout, @@query_cache_size AS query_cache_size,
@@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone,
@@tx_isolation AS tx_isolation, @@wait_timeout AS wait_timeout
2021-04-28T17:23:01.467362+08:00     7048 Query SHOW WARNINGS
2021-04-28T17:23:01.469893+08:00     7048 Query SET NAMES utf8mb4
2021-04-28T17:23:01.470325+08:00     7048 Query SET character_set_results = NULL
2021-04-28T17:23:01.470681+08:00     7048 Query SET autocommit=1
2021-04-28T17:23:01.580189+08:00     7048 Query SELECT 1
2021-04-28T17:23:01.584444+08:00     7048 Query select @@session.tx_read_only
2021-04-28T17:23:01.584964+08:00     7048 Query SELECT @@session.tx_isolation
······
2021-04-28T17:23:10.621839+08:00     7052 Quit
2021-04-28T17:23:12.623470+08:00     7051 Query SELECT 1
2021-04-28T17:23:12.624380+08:00     7053 Query SELECT 1
2021-04-28T17:23:14.625555+08:00     7053 Query SELECT 1
2021-04-28T17:23:14.626719+08:00     7051 Query SELECT 1
2021-04-28T17:23:16.627945+08:00     7051 Query SELECT 1
2021-04-28T17:23:16.628719+08:00     7053 Query SELECT 1
2021-04-28T17:23:18.629940+08:00     7053 Query SELECT 1
2021-04-28T17:23:18.630674+08:00     7051 Query SELECT 1

如果翻阅文章的老师们有对 Druid 探活或其余参数的钻研欢送后盾留言分割小编,程度无限,敬请您的赐教。

参考

https://github.com/alibaba/dr…

https://github.com/alibaba/dr…

鸣谢:

爱可生 CTO- 黄炎学生 以及 爱可生研发 - 孙健学生,感激两位老师对 Druid 测试提供的帮忙。

正文完
 0