最近一段时间在用 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,转载请注明出处。
-----------------
标签: NIO, Mina, socket leak
绿色通道:好文要顶关注我收藏该文与我联系
Jacklondon Chen
关注 - 0
粉丝 - 2
+加关注
1
0
(请您对文章做出评价)
« 博主前一篇:刚发布新的 web 单点登录系统,欢迎下载试用,欢迎提建议
» 博主后一篇:PostgreSQL 数据库在 Windows Server 2008 上安装注意事项
posted @ 2011-03-16 12:43 Jacklondon Chen 阅读(956) 评论(9) 编辑 收藏
评论
回复 引用 查看
#1楼2011-08-04 12:58 | zeitgeist
博主好,
我遇到了与你相似问题,问题还没解决,
我想问下上面步骤c 中的代码是写在客户端的还是写在服务器端
回复 引用 查看
#2楼[楼主]2011-08-05 19:18 | Jacklondon Chen
@zeitgeist
我这边代码都是服务器端代码,没有客户端代码。上面 h 提到NioSocketConnector.dispose(),我忘了写明在哪里调用,我会尽快更新补上。
不知道您的代码,是否也是类似于代理服务器。
回复 引用 查看
#3楼[楼主]2011-08-11 12:57 | Jacklondon Chen
引用zeitgeist:
博主好,
我遇到了与你相似问题,问题还没解决,
我想问下上面步骤c 中的代码是写在客户端的还是写在服务器端
我这边是在 IoHandlerAdapter 子类中,关闭对应的 connector:
@Override
public void sessionClosed(IoSession session) throws Exception {
IoConnector con = (IoConnector) session.getAttribute(SsoMinaUtils.IO_SESSION_CONTEXT_KEY_IO_CONNECTOR);
SsoMinaUtils.closeQuietly(con);
回复 引用 查看
#4楼2011-08-12 22:42 | zeitgeist
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样的做法有什么缺陷,麻烦博主能够给出一些建议。
再次感谢能够博主热心回复。
回复 引用 查看
#5楼2011-08-19 10:37 | happyblue
引用zeitgeist:
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样...
NioSocketConnector关闭的问题主要出现在 你的服务器即作为Mina的服务端同时也作为mina的客户端跟其他服务打交道的时候
回复 引用 查看
#6楼[楼主]2011-08-19 13:32 | Jacklondon Chen
引用zeitgeist:
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样...
zeitgeist,如果你的服务器端只提供搜索服务,那么应该不需要用 mina.
不太清楚你所说的客户端用 tomcat 是什么架构。
据我个人理解,如果你的服务器端和客户端,都是用 tomcat ,那么这两个系统中,根本不需要做什么通讯。Web/HTTP 协议中,本来就可以在页面中链接来链接去的。
回复 引用 查看
#7楼2011-09-09 10:24 | zj6882917
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
回复 引用 查看
#8楼[楼主]2011-09-09 22:08 | Jacklondon Chen
引用zj6882917:
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
能略微透漏一下你用 mina 写什么样的程序么?代理服务器?socket 服务器?socket 客户端?
回复 引用 查看
#9楼2011-10-30 13:53 | zj6882917
@Jacklondon Chen
引用Jacklondon Chen:
引用zj6882917:
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
能略微透漏一下你用...
感谢楼主关注我的恢复,我是用mina作socket服务器,在windows 2008下后非常多的本地端口连接本地端口的tcp连接,不知为何。
在rh6上面曾经发现过too many open files 问题,但是是因为我的代码问题,呵呵,有一路数据忘记close的缘故。
我们使用 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,转载请注明出处。
-----------------
标签: NIO, Mina, socket leak
绿色通道:好文要顶关注我收藏该文与我联系
Jacklondon Chen
关注 - 0
粉丝 - 2
+加关注
1
0
(请您对文章做出评价)
« 博主前一篇:刚发布新的 web 单点登录系统,欢迎下载试用,欢迎提建议
» 博主后一篇:PostgreSQL 数据库在 Windows Server 2008 上安装注意事项
posted @ 2011-03-16 12:43 Jacklondon Chen 阅读(956) 评论(9) 编辑 收藏
评论
回复 引用 查看
#1楼2011-08-04 12:58 | zeitgeist
博主好,
我遇到了与你相似问题,问题还没解决,
我想问下上面步骤c 中的代码是写在客户端的还是写在服务器端
回复 引用 查看
#2楼[楼主]2011-08-05 19:18 | Jacklondon Chen
@zeitgeist
我这边代码都是服务器端代码,没有客户端代码。上面 h 提到NioSocketConnector.dispose(),我忘了写明在哪里调用,我会尽快更新补上。
不知道您的代码,是否也是类似于代理服务器。
回复 引用 查看
#3楼[楼主]2011-08-11 12:57 | Jacklondon Chen
引用zeitgeist:
博主好,
我遇到了与你相似问题,问题还没解决,
我想问下上面步骤c 中的代码是写在客户端的还是写在服务器端
我这边是在 IoHandlerAdapter 子类中,关闭对应的 connector:
@Override
public void sessionClosed(IoSession session) throws Exception {
IoConnector con = (IoConnector) session.getAttribute(SsoMinaUtils.IO_SESSION_CONTEXT_KEY_IO_CONNECTOR);
SsoMinaUtils.closeQuietly(con);
回复 引用 查看
#4楼2011-08-12 22:42 | zeitgeist
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样的做法有什么缺陷,麻烦博主能够给出一些建议。
再次感谢能够博主热心回复。
回复 引用 查看
#5楼2011-08-19 10:37 | happyblue
引用zeitgeist:
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样...
NioSocketConnector关闭的问题主要出现在 你的服务器即作为Mina的服务端同时也作为mina的客户端跟其他服务打交道的时候
回复 引用 查看
#6楼[楼主]2011-08-19 13:32 | Jacklondon Chen
引用zeitgeist:
@Jacklondon Chen
博主你好
非常感谢你给的答复。我的服务现在暂时没有什么问题,但是我还是有2个 问题。
1 在你的文章中 h 步骤中提到的 NioSocketConnector也要关闭的,不知道你指的是在什么情况下关闭,肯定不是在IoSession关闭时关闭吧。
2 本人对Mina只是刚接触,对你说的代理服务器不是很清楚。我应该没用到。
我的服务大概是这样的
服务端:用来提供搜索服务,搜索功能依靠lucene.
客户端:tomcat服务器执行的代码作为客户端,发出搜索请求。
由于服务中其他模块的影响暂时只能将以上两部分放到同一个服务器上。
不知道这样...
zeitgeist,如果你的服务器端只提供搜索服务,那么应该不需要用 mina.
不太清楚你所说的客户端用 tomcat 是什么架构。
据我个人理解,如果你的服务器端和客户端,都是用 tomcat ,那么这两个系统中,根本不需要做什么通讯。Web/HTTP 协议中,本来就可以在页面中链接来链接去的。
回复 引用 查看
#7楼2011-09-09 10:24 | zj6882917
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
回复 引用 查看
#8楼[楼主]2011-09-09 22:08 | Jacklondon Chen
引用zj6882917:
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
能略微透漏一下你用 mina 写什么样的程序么?代理服务器?socket 服务器?socket 客户端?
回复 引用 查看
#9楼2011-10-30 13:53 | zj6882917
@Jacklondon Chen
引用Jacklondon Chen:
引用zj6882917:
博主你好,很高兴看到你的这篇博文,我的项目也有这个问题,但是这个bug只有windows 2008上产生,在rh6.0上面是没有的。代码是相同的。
再有就是,我在开发环境中断住IoHandlerAdapter子类中的exceptionCaught和sessionIdle,在没有任何客户连入的情况下,依然会长时间产生多个127.0.0.1的本地连接,但是exceptionCaught和sessionIdle事件从来没有被触发过。所以我貌似没办法用你的办法修补我服务中的问题。
请博主指点迷津。
能略微透漏一下你用...
感谢楼主关注我的恢复,我是用mina作socket服务器,在windows 2008下后非常多的本地端口连接本地端口的tcp连接,不知为何。
在rh6上面曾经发现过too many open files 问题,但是是因为我的代码问题,呵呵,有一路数据忘记close的缘故。