NIO中几个非常重要的技术点
来源:互联网 发布:零件的加工方法与编程 编辑:程序博客网 时间:2024/05/16 09:40
这些都是在实践中踩过雷的,今天某应用再次踩雷,把遇到的几个雷都收集一下,给后来者参考。
1.即使是accept事件,没有真正的read和write,Channel也要关闭,否则unix domain socket会被泄漏(WINDOWS更可怕),因为NIO的每个
Channel上都有两个FD用来监听事件(接收和发送走不同的FD)。
2.cancel事件导致CPU占用100%,http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
其原因就是调用key.cancel()时底层在下一次seelect前并没有真正的取消。导致等待select事件返回却又没有返回我们注册的key.这个事件不断地
循环触发,CPU一直处理返回 key为0的select()调用。解决方法有两种,一是在key.cancel()后立即selectNow();但是如果是多线程并发操作,有
可能这两行语句中间线程被切换,使得key.cancel()后没有立即执行 selectNow().这在多Selector情况下是可能的。另一种就是jetty处理方式,如果
select()返回0且连续几次出现这样的情况(有事件触发返回,却不是返回我们注册的KEY),就将有效的key重新注册到一个新的selector上。其实
glassfish在处理多次次次次write返回为0的情况时也是这种策略。
示例代码:(真实的项目中)
int selectTimeout = connectionConfig.getSelectTimeout(); int allProcessMaxTime = connectionConfig.getAllProcessMaxTime(); //selector在实现时有bug,epool底层可能会发送一个错误的信号导致select方法提前返回,但没有 //返回注册的事件,而且不断循环造成CPU100% int slelectZeroCount = 0; int maxZeroCount = 20; int fixed = 0; while (selector.isOpen() && selector.keys().size() != 0 && allProcessMaxTime > 0) { long start = System.currentTimeMillis(); // 查询看是否有已经准备好的通道,指定超时时间 int count = selector.select(selectTimeout); if (count == 0) { slelectZeroCount++; } else { slelectZeroCount = 0; //保证是连续的count==0时才将slelectZeroCount++,如果其中有一次返回注册事件测已经正常 } if (slelectZeroCount > maxZeroCount && fixed == 0) { //没有尝试修复动作,则先进行修复干预 for (SelectionKey key : selector.keys()) { if (key.isValid() && key.interestOps() == 0) { key.cancel(); } } fixed = 1; } else if (slelectZeroCount > maxZeroCount && fixed == 1) { //如果已经干预过仍然连续返回0,注意如果不返回0的话slelectZeroCount就被置0. //重新获取一个selector,将当前事件重新注册到新的selector上。并销毁当前selector Selector newSelector = this.getSelector(); this.changeSelector(selector, newSelector); selector = newSelector; } //对channel进行正常处理 }
重新注册的代码:
private synchronized void changeSelector(Selector oldSelector, Selector newSelector) { for (SelectionKey key : oldSelector.keys()) { if (!key.isValid() || key.interestOps() == 0) { continue; } Object att = key.attachment(); try { if (att == null) { key.channel().register(newSelector, key.interestOps()); } else { key.channel().register(newSelector, key.interestOps(), att); } } catch (ClosedChannelException e) { SocketChannel sc = (SocketChannel) key.channel(); sc.close(); } } try { oldSelector.close(); } catch (IOException e) { logger.error(e.getMessage()); } }
同样对于网络状态不好时,连续写操作返回0的处理:
private void flushData(Selector selector, SocketChannel socketChannel, ByteBuffer byteBuffer) throws IOException { int count = 0; int maxCount = 20; while (byteBuffer.hasRemaining()) { int len = socketChannel.write(byteBuffer); if (len < 0) { throw new EOFException("write channel is closed."); } // 如果不对len==0(即当前网络不可用)的情况处理,则while(byteBuffer.hasRemaining())可能一直 // 循环下去而消耗大量的CPU. if (len == 0) { count++; } else { count = 0; } if (count > maxCount) { throw new IOException("can't connect to target."); } } }
- NIO中几个非常重要的技术点
- NIO中几个非常重要的技术点
- 2. JMeter中非常重要的几个概念
- 非常重要的技术博客地址
- Windows下配置Caffe+Tensorflow几个非常重要的注意点和所需安装包!!
- 多线程技术还是非常重要的
- .NET项目开发的几个非常重要的项目设置
- .NET项目开发的几个非常重要的项目设置
- JAVA NIO技术(中)
- 工作中非常重要的十句话!
- 产品中非常重要的 Alpha版本
- Hibernate中5个非常重要的接口
- c++中几个需要注意的点
- 一场成功的技术面试的几个关键点
- 几个小技术点总结
- 随手记几个技术点
- 18、集合(对java来说非常重要的技术)
- 18、集合(对java来说非常重要的技术)
- Page的ResolveClientUrl与ResolveUrl读取路径
- android webView判断是否加载完成
- 面试题==与equals的区别
- MPC8572调试记录
- 百Google度搜索
- NIO中几个非常重要的技术点
- ASP.NET2.0中Page.ClientScript.RegisterClientScriptBlock与RegisterClientScriptBlock
- Android应用模块之间的交互方式和接口定义
- java线程(二) 实现
- 【译】MVC3 20个秘方-(2)通过脚手架自动生成controller和view ----代码先行/数据库先行
- 用汇编的眼光看C++(之退出流程)
- 用 PHP 调用 FCKeditor
- 键盘钩子,游戏外挂基础
- linux下 tar解压 gz解压 bz2等各种解压文件使用方法