日期:2014-05-17  浏览次数:21121 次

zz:Apache NIO 框架 Mina 使用中出现 too many open files 问题的解决办法

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 ,转载请注明出处。