前言
最近用了公司某框架,部署到現場後,現場運維開始維護現場資料,在不斷操作的過程中,系統崩潰,檢視後臺日誌,druid連線池已經獲取不到連線。於是開始了排查之旅。在此記錄。
排查開始
首先後臺的報錯是這樣的。
exception=org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 10, maxActive 10
第一反應maxActive設定的數量太少了。於是改為100。重新啟動,並再次操作大量資料。發現過了一段時間100個也滿了。
此時問題不簡單了。看來是有程式碼用了程式連線後,沒有釋放。
接下來開始確認原因到底是不是有沒有釋放。
在專案中使用的druid連線池。druid連線池是自帶圖形化監控工具的。於是開始在專案中配置,啟動druid連線池。
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean srb =
new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//設定控制檯管理使用者
srb.addInitParameter("loginUsername","root");
srb.addInitParameter("loginPassword","root");
//是否可以重置資料
srb.addInitParameter("resetEnable","false");
return srb;
}
/**
* 註冊FilterRegistrationBean
* @return
*/
@Bean
public FilterRegistrationBean druidStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
//新增過濾規則.
bean.addUrlPatterns("/*");
//新增不需要忽略的格式資訊.
bean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return bean;
}
}
重新啟動後,訪問:localhost:8081/druid。這個路徑有人大概會懷疑如果我是通過閘道器管理的微服務框架,需要通過閘道器轉發訪問嗎?其實大可不必,通過閘道器也可以,也是跳轉到直接訪問的地址。
進入訪問地址,跳轉到如下的登入介面。
輸入剛才在程式碼中配置的使用者名稱和密碼,則可以成功進入。
我們此時要點選資料來源,去關注建立的邏輯連線數和關閉的邏輯連線數,關鍵的指標在這裡,如果連線池的開啟和關閉是正常的,那麼二者的值應該是相等的。
再看看此時的活躍連線數
為0,此時正常,因為還沒有進行操作。
接下來開始對之前的操作進行復現,鎖定具體的操作。重複之前現場運維所做操作。
通過不斷的點選功能,縮小功能範圍,最終發現,只要點選左側樹,就會造成邏輯開啟連線和邏輯關閉次數不一致。
活躍連線數也到了二者之差。等了幾分鐘,仍然是這個情況。那麼實錘了 這裡的程式碼有問題,連線應該沒有釋放。那麼程式碼那麼多,該如何發現具體程式碼的位置呢。
接下來配置druid的abandon策略。通過abandon可以強制回收資料庫的連線。而活躍的連線被回收則會列印堆疊資訊,這是就知道是哪裡的sql程式碼沒有釋放了。
配置如下:
spring:
datasource:
druid:
remove-abandoned: true
remove-abandoned-timeout: 30
log-abandoned: true
重啟專案,這個時候durid的監控活躍連線數的功能就可以看到程式碼資訊。
我們點選如下位置:
就會彈出上圖的堆疊資訊。打馬賽克的地方就是程式碼的詳細位置,會標記出來。開發人員去響應的類找到相應程式碼檢視即可。
經過檢視程式碼發現,程式碼的連線釋放存在問題。是自己封裝的sql查詢類。改為mybatis的寫法後,問題解決。