Druid配置參數(shù)詳解-removeAbandoned,logAbandoned,removeAbandonedTimeoutMillis
Druid是一個(gè)由阿里開(kāi)源的數(shù)據(jù)庫(kù)連接池,Druid的配置非常豐富,但是設(shè)置不當(dāng)會(huì)對(duì)生產(chǎn)環(huán)境造成嚴(yán)重影響,網(wǎng)上Druid的資料雖多,但大部分都是互相復(fù)制粘貼,有很多不準(zhǔn)確甚至完全錯(cuò)誤的描述,Druid已經(jīng)開(kāi)源很久,而且作者WenShao的工作重心也已經(jīng)不在Druid上,有些功能估計(jì)他自己都不太了解了。本系列將從源代碼的角度分析Druid目前的最新版本(1.1.21)各個(gè)常用的配置項(xiàng)的具體含義以及是怎么起作用的。
畫(huà)外音:目前Druid在開(kāi)源中國(guó)舉辦的2019年度最受歡迎中國(guó)開(kāi)源軟件中排名第7名,支持Druid的朋友可以去投票哇。2019年度最受歡迎中國(guó)開(kāi)源軟件
removeAbandoned,logAbandoned,removeAbandonedTimeoutMillis是什么意思?
removeAbandoned:如果連接泄露,是否需要回收泄露的連接,默認(rèn)false;
logAbandoned:如果回收了泄露的連接,是否要打印一條log,默認(rèn)false;
removeAbandonedTimeoutMillis:連接回收的超時(shí)時(shí)間,默認(rèn)5分鐘;
畫(huà)外音:這兩個(gè)參數(shù)筆者認(rèn)為非常重要,但是不知為何,Druid默認(rèn)是不開(kāi)啟的,并且官方的配置例子中也沒(méi)有配置這兩個(gè)參數(shù):Druid官方配置,筆者就因?yàn)闆](méi)有配置這兩個(gè)參數(shù),曾經(jīng)吃過(guò)大虧。
removeAbandoned,logAbandoned,removeAbandonedTimeoutMillis有什么用?
舉個(gè)栗子:筆者當(dāng)初做過(guò)單個(gè)事務(wù)使用多數(shù)據(jù)源的功能,重寫(xiě)了Spring的ConnectionHolder,由于筆者的疏忽,每次從ConnectionHolder中獲取連接時(shí)都獲取到了兩條連接,但是只是使用了其中的一條,相當(dāng)于另一條連接只是從連接池中拿出來(lái)了,但是再也不會(huì)還回去了,這樣就導(dǎo)致了連接池中的連接很快就消耗光了,即activeCount=maxActive。
如果當(dāng)時(shí)我設(shè)置了removeAbandoned就不會(huì)出現(xiàn)這個(gè)問(wèn)題,應(yīng)該Druid會(huì)定期檢查池中借出去的連接是否處于運(yùn)行狀態(tài),如果不是處于運(yùn)行狀態(tài),并且借出時(shí)間超過(guò)removeAbandonedTimeoutMillis(默認(rèn)5分鐘)就會(huì)回收該連接。
連接是怎么回收的?
Druid每隔timeBetweenEvictionRunsMillis(默認(rèn)1分鐘)會(huì)調(diào)用DestroyTask,在這里會(huì)判斷是否可以回收泄露的連接
public class DestroyTask implements Runnable {
public DestroyTask() {
}
@Override
public void run() {
shrink(true, keepAlive);
//判斷removeAbandoned是否為true,默認(rèn)是false,不知為何
if (isRemoveAbandoned()) {
removeAbandoned();
}
}
}
for (; iter.hasNext();) {
DruidPooledConnection pooledConnection = iter.next();
//判斷該連接是否還在運(yùn)行,只回收不運(yùn)行的連接
//Druid會(huì)在連接執(zhí)行query,update的時(shí)候設(shè)置為正在運(yùn)行,
// 并在回收后設(shè)置為不運(yùn)行
if (pooledConnection.isRunning()) {
continue;
}
long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
//判斷連接借出去的時(shí)間大小
if (timeMillis >= removeAbandonedTimeoutMillis) {
iter.remove();
pooledConnection.setTraceEnable(false);
abandonedList.add(pooledConnection);
}
}
//判斷是否要記錄連接回收日志,這個(gè)很重要,可以及時(shí)發(fā)現(xiàn)項(xiàng)目中是否有連接泄露
if (isLogAbandoned()) {
StringBuilder buf = new StringBuilder();
buf.append("abandon connection, owner thread: ");
buf.append(pooledConnection.getOwnerThread().getName());
buf.append(", connected at : ");
buf.append(pooledConnection.getConnectedTimeMillis());
buf.append(", open stackTrace\n");
}
總結(jié)
筆者認(rèn)為這三個(gè)參數(shù)非常重要,但是官方默認(rèn)是不開(kāi)啟的,建議使用Druid的項(xiàng)目都設(shè)置這三個(gè)參數(shù),這樣可以有效的發(fā)現(xiàn)代碼中是否有連接泄露。