Druid连接Impala持续失败原因调查

3,874 阅读3分钟

一个使用Druid连接Impala的服务项目实例抛出了一个com.alibaba.druid.pool.GetConnectionTimeoutException异常,当时的连接池监控数据如下:

wait millis 5000, active 0, maxActive 50, creating 1

看似是普通的获取连接超时的问题,但该实例在随后一直保持对Impala获取连接失败的状态,同一个项目下的其他实例并没有发生类似的连接问题,可以排除Impala服务器端的问题,问题定位范围是在通讯网络或者Impala客户端(项目实例)上。

持续不断的运行日志打印都现实creating 1这个信息,这表示Druid一直保持创建(物理/TCP)连接的状态,这里需要先说明一下Druid创建连接的机制。

Druid建立底层连接的方法是调用DruidDataSource.createPhysicalConnection()方法实现的,调用的时机和方式有以下几种:

  • 初始化时直接调用
  • CreateConnectionThread 线程实例调用
  • CreateConnectionTask 任务实例调用

当用户没有设置 createScheduler, 即 createScheduler == null 时, 后续所有连接的创建工作是由 CreateConnectionThread 线程实例去完成。该线程的创建只出现在一个方法中:

protected void createAndStartCreatorThread() {
        if (createScheduler == null) {
            String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this);
            createConnectionThread = new CreateConnectionThread(threadName);
            createConnectionThread.start();
            return;
        }

        initedLatch.countDown();
    }

该方法仅在初始化DruidDataSource.init()方法调用过一次,即连接创建线程数量为 1

同时,在项目的配置如下:

initialSize = 0
minIdle = 0
minEvictableIdleTimeMillis = 0
maxEvictableIdleTimeMillis = 0
maxActive = 50
maxWait = 5000
isTestOnBorrow = false
isLogAbandoned
isRemoveAbandoned = true
removeAbandonedTimeout = 60 * 10

针对连接超时的配置仅有maxActive参数,表示获取连接的最长时间,该参数在Druid实现,是获取连接的主线程等待连接返回而循环休眠的最大时长。

在项目中,使用jstack命令查看CreateConnectionThread线程的状态

"Druid-ConnectionPool-Create-1942750238" #122 daemon prio=5 os_prio=31 tid=0x00007ff2ba81c000 nid=0x8907 runnable [0x0000700009ec7000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	- locked <0x00000007b01c1240> (a java.io.BufferedInputStream)
	at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:127)

该线程被阻塞在了一个方法上,问题就在这里:创建连接的线程被阻塞在等待与Impala建连的通讯数据上,后续所有连接的获取都必然会自然等待到超时。那么该线程的状态可以恢复吗?

模拟一下这种异常情况,使用nc -l ${impala-port}监听本地的端口,同时用将连接url的地址host到本地,运行程序。因为本地不存在Impala服务,所以自然响应不了这个建连过程,同时我们将url修改host至正确的Impala地址,会发现CreateConnectionThread线程会一直保持被阻塞状态,没有恢复(取消本地端口监听可以时线程脱离阻塞状态)。

那么如何解决这个问题呢?主要是避免建连线程一直阻塞,有几个方向:

  • 设置建连过程的超时:此类参数是Driver的连接参数,有read timeoutsocket timeout
  • 设置createScheduler变量,避免单线程建连

TODO HiveDriver 建连代码分析