2023年11月29日发(作者:)

记录⼀次springbootdruid连接池配置信息错误,⼀直重试连接的问题

------------恢复内容开始------------

在昨天项⽬开发中,使⽤了Druid作为数据库连接池,当数据源密码错误时,报出了以下错误:

2019-04-09 10:09:36 [Druid-ConnectionPool-Create-2053591126] [ ataSource ] [ 53 ] [ ERROR ] create connection

SQLException, url: jdbc:mysql://*.*.*.*:3306/*?characterEncoding=utf-8&useSSL=false, errorCode 1045, state 28000

eption: Access denied for user 'malluser'@'*.*.*.*' (using password: YES)

at SQLException(:545) ~[:6.0.6]

at SQLException(:513) ~[:6.0.6]

at ateException(:115) ~[:6.0.6]

at NewIO(:1606) ~[:6.0.6]

at tionImpl.(:633) ~[:6.0.6]

at tance(:347) ~[:6.0.6]

at t(:219) ~[:6.0.6]

at tion_connect(:149) ~[druid-1.1.6%:1.1.6]

at tion_connect(:218) ~[druid-1.1.6%:1.1.6]

at tion_connect(:143) ~[druid-1.1.6%:1.1.6]

at PhysicalConnection(:1512) ~[druid-1.1.6%:1.1.6]

at PhysicalConnection(:1575) ~[druid-1.1.6%:1.1.6]

at ataSource$(:2450) [druid-1.1.6%:1.1.6]

对于错误相信⼤家⼀看都明⽩是怎么回事,但是问题是Druid会⼀直不停的对数据源进⾏重试连接,这样的话我们的⽇志很快就爆了,为什么Druid会⼀直进⾏重试呢?⾸先让我们看

⼀下源码:

public class CreateConnectionTask implements Runnable {

private int errorCount = 0;

@Override

public void run() {

runInternal();

}

private void runInternal() {

for (;;) {

// addLast

();

try {

if (closed || closing) {

createTaskCount--;

return;

}

boolean emptyWait = true;

if (createError != null && poolingCount == 0) {

emptyWait = false;

}

if (emptyWait) {

try {

physicalConnection = createPhysicalConnection();

setFailContinuous(false);

} catch (OutOfMemoryError e) {

("create connection OutOfMemoryError, out memory. ", e);

errorCount++;

if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {

// fail over retry attempts

setFailContinuous(true);

if (failFast) {

();

try {

All();

} finally {

();

}

}

if (breakAfterAcquireFailure) {

();

try {

createTaskCount--;

} finally {

();

}

} catch (RuntimeException e) {

("create connection RuntimeException", e);

// unknow fatal exception

setFailContinuous(true);

continue;

} catch (Error e) {

();

try {

createTaskCount--;

} finally {

();

}

("create connection Error", e);

// unknow fatal exception

setFailContinuous(true);

break;

} catch (Throwable e) {

("create connection unexecpted error.", e);

break;

}

if (physicalConnection == null) {

continue;

}

boolean result = put(physicalConnection);

if (!result) {

(sicalConnection());

("put physical connection to pool failed.");

}

break;

}

}

}

public class CreateConnectionThread extends Thread {

public CreateConnectionThread(String name){

super(name);

this.setDaemon(true);

}

public void run() {

own();

long lastDiscardCount = 0;

int errorCount = 0;

for (;;) {

// addLast

try {

terruptibly();

} catch (InterruptedException e2) {

}

if (emptyWait) {

// 必须存在线程等待,才创建连接

if (poolingCount >= notEmptyWaitThreadCount //

&& !(keepAlive && activeCount + poolingCount < minIdle)) {

();

}

// 防⽌创建超过maxActive数量的连接

if (activeCount + poolingCount >= maxActive) {

();

continue;

}

}

} catch (InterruptedException e) {

lastCreateError = e;

lastErrorTimeMillis = tTimeMillis();

if (!closing) {

("create connection Thread Interrupted, url: " + jdbcUrl, e);

}

break;

} finally {

();

}

PhysicalConnectionInfo connection = null;

try {

connection = createPhysicalConnection();

setFailContinuous(false);

} catch (SQLException e) {

("create connection SQLException, url: " + jdbcUrl + ", errorCode " + orCode()

+ ", state " + State(), e);

errorCount++;

if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {

// fail over retry attempts

setFailContinuous(true);

if (failFast) {

();

try {

continue;

}

boolean result = put(connection);

if (!result) {

(sicalConnection());

("put physical connection to pool failed.");

}

errorCount = 0; // reset errorCount

}

}

}

从源码中我们可以看到,线程中使⽤了⽆参for循环再⼀直尝试进⾏数据源连接,代码中【errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis

> 0】当满⾜该判断条件时就会进⾏重试连接,接下来我们看⼀下源码中这两个属性值设置的是什么呢?(源码过长,只展⽰我们需要的代码,其他属性信息可⾃⾏扒源码~)

private static final long serialVersionUID = 1L;

public final static int DEFAULT_INITIAL_SIZE = 0;

public final static int DEFAULT_MAX_ACTIVE_SIZE = 8;

public final static int DEFAULT_MAX_IDLE = 8;

public final static int DEFAULT_MIN_IDLE = 0;

public final static int DEFAULT_MAX_WAIT = -1;

public final static String DEFAULT_VALIDATION_QUERY = null; //

public final static boolean DEFAULT_TEST_ON_BORROW = false;

public final static boolean DEFAULT_TEST_ON_RETURN = false;

public final static boolean DEFAULT_WHILE_IDLE = true;

public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = 60 * 1000L;

public static final long DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS = 500;

public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3;

/*****************************华丽的分割线中间省略代码若⼲⾏*********************************/

protected volatile long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;

protected int connectionErrorRetryAttempts = 1;

protected boolean breakAfterAcquireFailure = false;

从中这段代码中我们可以看到connectionErrorRetryAttempts值为1,timeBetweenConnectErrorMillis值为60000,⽽breakAfterAcquireFailure值为false,因此当我们数据

源连接失败后,就会不断的进⾏重试连接,因此我对于对于该如何解决这样的问题我们就有了答案:

1.若不想让重试,我们可以设置breakAfterAcquireFailure(true);connectionErrorRetryAttempts(0);

2.若想要设置多久重试,我们只需要设置timeBetweenConnectErrorMillis(time);

action:经过亲测,直接在配置⽂件中配置属性并不能读取到(Druid设计时就这样,⼤神的思维暂时还不能参悟~),我们可直接将值写⼊程序当中,如下:

private static void Init() {

try {

Properties properties = loadPropertiesFile("ties");

druidDataSource = (DruidDataSource) DataSource(properties); // DruidDataSrouce⼯⼚模式

// TODO 调试配置,⽤完删除

oveAbandoned(true);

oveAbandonedTimeout(600);

Abandoned(true);

// akAfterAcquireFailure(true);

eBetweenConnectErrorMillis(60000);

// nectionErrorRetryAttempts(0);

} catch (Exception e) {

(DbPoolConnection.class, e);

}

}

当然若是想要⽅便以后修改,我们可以在properties⽂件中设置该值,读取出来配置⽂件的值再赋值给druidDataSource。

到这⾥,本⽂就结束了,如有不当之处,欢迎指正!