同事牛逼啊,寫了個隱藏 bug,我排查了 3 天才解決問題!

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
最近線上監(jiān)控 SFTP 連接頻繁爆表,通過重啟某個系統(tǒng),連接數(shù)迅速下降,系統(tǒng)就能恢復(fù)正常,初步判斷是應(yīng)用程序連接未關(guān)閉的問題導(dǎo)致的。
棧長通過 IDE 全局搜索排查,SFTP 連接使用了 jsch 包,確實有一些功能點使用了 SFTP 連接而未關(guān)閉的情況,或者不在 finally 語句塊中正常關(guān)閉。
整改上線后,SFTP 還是爆表……
事后運維心態(tài)都要崩了,運維主動寫了個 SFTP 連接監(jiān)控,當(dāng)連接超過 5 分鐘空閑時就主動斷開。
但這只是臨時的處理,真正的原因肯定還是應(yīng)用程序沒有正常關(guān)閉導(dǎo)致的,于是再認(rèn)真排查下程序,終于找出了元兇。。
下面是示例代碼:
Session?session?=?null;
ChannelSftp?channel?=?null;
try{
????for(...){
????????...
????????//?創(chuàng)建會話
????????JSch?jsch?=?new?JSch();
????????jsch.getSession(host,?username);
????????session?=?jsch.getSession(username,?host,?port);
????????session.setPassword(password);
????????session.connect();
????????
????????//?創(chuàng)建sftp連接
????????channel?=?session.openChannel("sftp");
????????channel.connect();
????????...
????}
}?catch(...){
????...
}?finally{
????if?(null?!=?channel?&&?channel.isConnected())?{
????????channel.disconnect();
????}
????if?(null?!=?session?&&?session.isConnected())?{
????????session.disconnect();
????}
}
大家都看出問題了嗎?
這程序我檢查了 2 遍,排查了 3 天才解決了這個問題。
寫出這代碼,同事真是個人才?。。?!
乍一看,連接確實是關(guān)閉了啊,也確實是在 finally 語句塊關(guān)閉,為什么還會有問題?
原因就是在該死的 for 循環(huán)中創(chuàng)建連接的,雖然在 finally 中進(jìn)行了關(guān)閉,但是連接變量在循環(huán)中進(jìn)行重建和替換,所以關(guān)閉的永遠(yuǎn)只是最后一個連接。
而且,這還是個下載 Excel 明細(xì)的功能,數(shù)據(jù)很多的時候,一個操作就能導(dǎo)致連接瞬間爆表。
解決方案肯定是要把創(chuàng)建連接的部分拿到 for 循環(huán)前面去,連接創(chuàng)建一次就好了,可以反復(fù)使用。
另外,知道 JDK 7+ 中的 try-with-resources 語法的朋友可能會問,可以省略 finally 語句塊吧,可以直接在 try(...) 中定義,它會自動關(guān)閉。
Really?建議還是仔細(xì)閱讀下 try-with-resources 這篇文章吧,沒看過的可以關(guān)注公眾號Java技術(shù)棧進(jìn)行搜索閱讀。
這個 jsch 連接包還真不行,我們來看它的源碼吧,不然又是一個坑你沒商量的坑。
com.jcraft.jsch.Session:

com.jcraft.jsch.Channel:

這兩個類只實現(xiàn)了 Runnable 接口,沒有實現(xiàn) java.lang.AutoCloseable 接口,所以,它并不符合 try-with-resources 自動關(guān)閉的原則。關(guān)于流關(guān)閉具體演進(jìn)可以參考Java技術(shù)棧公眾號 "簡化流關(guān)閉新姿勢" 這篇文章。
至此,線上 SFTP 連接爆表的問題終于解決了,可以安心睡個好覺了,同時,我也感覺我們的同事太牛逼了,又讓我漲知識了。
大家引以為戒吧,也歡迎在看、轉(zhuǎn)發(fā)!
點擊「閱讀原文」獲取面試題大全~
