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.
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。
到这⾥,本⽂就结束了,如有不当之处,欢迎指正!


发布评论