Apache NIO 框架 Mina 使用中出現 too many open files 問題的解決辦法
最近一段時間在用 Apache NIO 框架 Mina, 用起來感覺不錯。
我們使用 Apache NIO 作了一個 TCP server, 來處理 TCP 數據包。
只是最近突然發現 server 經常連接不上,每周一兩次。用戶沒有進行屏幕截圖就直接重新啟動,沒有找到第一手的故障現場資料。
開始以為是 JDK 及其他 Java 包 版本問題,連續升級了幾次,問題依舊。
后來終于在客戶現場抓個現行。屏幕截圖、備份日志文件后,逐個 ping/telnet 各個服務器及其端口。發現都沒有問題,奇怪了。突然想起,用 netstat 看看網絡連接狀態(windows server 2008), 發現大量的 127.0.0.1 到 127.0.0.1 的連接,狀態為 ESTABLISHED , 端口看起來是逐步增加的。
再看日志文件,發現寫出來的是 "too many open files” 導致 socket 連接不能建立。
網上搜索 google ,發現報告此問題的人不少,卻沒有人有解決方法。Apache Mina 網站上的 FAQ 也提到這個問題,說是要更改 windows 注冊表,簡直是胡扯。只能自己慢慢調查了。
這是一個類似于內存泄露的問題,只不過這里是 socket 未關閉導致。英文名詞為 : “socket leak”。
經過幾天的調試,發現了解決辦法,特記錄下來,供大家參考。
a. 使用到 NioSocketAcceptor 一個,用來 listen ,沒有問題。
b. 自定義 IoHandlerAdapter 在 Mina 中是 Singleton, 只創建一次,也沒有問題。
c. 自定義 IoHandlerAdapter 中需要有以下代碼:
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
session.close(true);//force close right now
}
public void sessionOpened(IoSession session) throws Exception {
session.getConfig().setBothIdleTime(180);//set timeout seconds, must
}
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
session.close(true);//timeout now, close it
}
d. 因代碼中使用了類似代理服務器的程序,需要特別處理。這里特別強調一下,Mina 自帶的 proxy 程序沒有實用價值。它使用的是多個客戶端連接,服務器只用一個 NioSocketConnector 轉發,這很成問題。如果 connector 斷開了,豈不是影響很大?因此需要改成每個 客戶端連接 對應一個 connector 的轉發模式。
e. NioSocketConnector 并非 thread safe, 這點 Mina 文檔中只字不提。很讓人抓狂。
f. 系統中使用了以下 Mina NIO 的 Java 對象:
NioSocketAcceptor 用來 listen, 1個,配 1 個 IoHandlerAdapter
每次 1 客戶 socket 連接,對應1 個 IoSession, 創建 1 個 NioSocketConnector 連接后端服務器,然后自動創建一個 IoSession 作為當前客戶 socket 的 peer (同伴),也就是兩個 IoSession 有對應關系。
g. 無論是客戶 socket 連接的 IoSession 還是 peer IoSession , 在 sessionClosed 中需要調用 peer.close(false); 這里的 close(false) 不是立即關閉,而是讓 peer 發完數據再關閉。這是由 NIO 這種異步操作特性決定的。這里不會造成死循環: client IoSession 調用 peer.close(false), 而 peer 反過來調用 client IoSession.close(false)。好像 Mina 做了特別處理。
h. 特別的提醒,NioSocketConnector 也要關閉。函數名是 dispose()。這點特別重要。這次出現 too many open files 的問題根源在這里。而 Mina 文檔中只字不提。而 NioSocketConnector 與 peer IoSession 使用 127.0.0.1 端隨機端口連接,匪夷所思。
而 peer IoSession 關閉后,沒有關閉 NioSocketConnector , 也沒有給它發 close event, 或者讓它進入 exception, 這種設計也不好。 IoSession 關閉后,留著 NioSocketConnector 也是無用,還白白成了一個 ESTABLISHED 狀態的連接,導致 socket leak。 這似乎就是所謂的半開 socket ? 還是覺得 Mina 這種設計不好。
上面寫的是問題故障解決辦法,特與大家分享。希望其它碰到此問題的人,少走點彎路。
最后總結,Mina 總體設計不錯,代碼質量也還好,我報告過一次 bug, 開發團隊也能很快回復。只是發現文檔欠缺,例子都是“示意”,意思意思而已,不能直接用起來。上手有些門檻。
發表于 http://jacklondon.cnblogs.com,轉載請注明出處。
-----------------

浙公網安備 33010602011771號