同事牛逼啊,寫了個隱藏 bug,我排查了 3 天才解決問題!
最近線上監控 SFTP
連線頻繁爆表,通過重啟某個系統,連線數迅速下降,系統就能恢復正常,初步判斷是應用程式連線未關閉的問題導致的。
棧長通過 IDE 全域性搜尋排查,SFTP 連線使用了 jsch
包,確實有一些功能點使用了 SFTP
連線而未關閉的情況,或者不在 finally
語句塊中正常關閉。
整改上線後,SFTP 還是爆表……
事後運維心態都要崩了,運維主動寫了個 SFTP
連線監控,當連線超過 5 分鐘空閒時就主動斷開。
但這只是臨時的處理,真正的原因肯定還是應用程式沒有正常關閉導致的,於是再認真排查下程式,終於找出了元凶。。
下面是示例程式碼:
Session session = null; ChannelSftp channel = null; try{ for(...){ ... // 建立會話 JSch jsch = new JSch(); jsch.getSession(host, username); session = jsch.getSession(username, host, port); session.setPassword(password); session.connect(); // 建立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 天才解決了這個問題。
寫出這程式碼,同事真是個人才啊!!!
乍一看,連線確實是關閉了啊,也確實是在 finally
語句塊關閉,為什麼還會有問題?
原因就是在該死的 for
迴圈中建立連線的,雖然在 finally
中進行了關閉,但是連線變數在迴圈中進行重建和替換,所以關閉的永遠只是最後一個連線。
而且,這還是個下載 Excel
明細的功能,資料很多的時候,一個操作就能導致連線瞬間爆表。
解決方案肯定是要把建立連線的部分拿到 for
迴圈前面去,連線建立一次就好了,可以反覆使用。
另外,知道 JDK 7+ 中的 try-with-resources
finally
語句塊吧,可以直接在 try(...)
中定義,它會自動關閉。
Really?建議還是仔細閱讀下 try-with-resources 這篇文章吧,沒看過的可以關注公眾號Java技術棧進行搜尋閱讀。
這個 jsch
連線包還真不行,我們來看它的原始碼吧,不然又是一個坑你沒商量的坑。
com.jcraft.jsch.Session:
com.jcraft.jsch.Channel:
這兩個類只實現了 Runnable
介面,沒有實現 java.lang.AutoCloseable
介面,所以,它並不符合 try-with-resources
自動關閉的原則。關於流關閉具體演進可以參考Java技術棧公眾號 "
至此,線上 SFTP
連線爆表的問題終於解決了,可以安心睡個好覺了,同時,我也感覺我們的同事太牛逼了,又讓我漲知識了。
大家引以為戒吧,也歡迎在看、轉發!
關注公眾號Java技術棧回覆"面試"獲取我整理的2020最全面試題及答案。
推薦去我的部落格閱讀更多:
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
覺得不錯,別忘了點贊+轉發哦!