`
zhoushijun
  • 浏览: 267593 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

NIO中几个非常重要的技术点

    博客分类:
  • java
阅读更多

这些都是在实践中踩过雷的,今天某应用再次踩雷,把遇到的几个雷都收集一下,给后来者参考。

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的情况时也是这种策略。

 

示例代码:(真实的项目中)

  1. int selectTimeout = connectionConfig.getSelectTimeout();  
  2. int allProcessMaxTime = connectionConfig.getAllProcessMaxTime();  
  3. //selector在实现时有bug,epool底层可能会发送一个错误的信号导致select方法提前返回,但没有  
  4. //返回注册的事件,而且不断循环造成CPU100%  
  5. int slelectZeroCount = 0;  
  6. int maxZeroCount = 20;  
  7. int fixed = 0;  
  8.   
  9. while (selector.isOpen() && selector.keys().size() != 0 && allProcessMaxTime > 0) {  
  10.     long start = System.currentTimeMillis();  
  11.     // 查询看是否有已经准备好的通道,指定超时时间  
  12.     int count = selector.select(selectTimeout);  
  13.   
  14.     if (count == 0) {  
  15.         slelectZeroCount++;  
  16.     } else {  
  17.         slelectZeroCount = 0;  
  18.         //保证是连续的count==0时才将slelectZeroCount++,如果其中有一次返回注册事件测已经正常  
  19.     }  
  20.     if (slelectZeroCount > maxZeroCount && fixed == 0) {  
  21.         //没有尝试修复动作,则先进行修复干预  
  22.         for (SelectionKey key : selector.keys()) {  
  23.             if (key.isValid() && key.interestOps() == 0) {  
  24.                 key.cancel();  
  25.             }  
  26.         }  
  27.         fixed = 1;  
  28.     } else if (slelectZeroCount > maxZeroCount && fixed == 1) {  
  29.         //如果已经干预过仍然连续返回0,注意如果不返回0的话slelectZeroCount就被置0.  
  30.         //重新获取一个selector,将当前事件重新注册到新的selector上。并销毁当前selector  
  31.         Selector newSelector = this.getSelector();  
  32.         this.changeSelector(selector, newSelector);  
  33.         selector = newSelector;  
  34.     }  
  35.     //对channel进行正常处理  


重新注册的代码:

  1. private synchronized void changeSelector(Selector oldSelector, Selector newSelector) {  
  2.         for (SelectionKey key : oldSelector.keys()) {  
  3.             if (!key.isValid() || key.interestOps() == 0) {  
  4.                 continue;  
  5.             }  
  6.             Object att = key.attachment();  
  7.             try {  
  8.                 if (att == null) {  
  9.                     key.channel().register(newSelector, key.interestOps());  
  10.                 } else {  
  11.                     key.channel().register(newSelector, key.interestOps(), att);  
  12.                 }  
  13.             } catch (ClosedChannelException e) {  
  14.                 SocketChannel sc = (SocketChannel) key.channel();  
  15.                 sc.close();  
  16.             }  
  17.         }  
  18.         try {  
  19.             oldSelector.close();  
  20.         } catch (IOException e) {  
  21.             logger.error(e.getMessage());  
  22.         }  
  23.   
  24.     }  


同样对于网络状态不好时,连续写操作返回0的处理:

  1. private void flushData(Selector selector, SocketChannel socketChannel, ByteBuffer byteBuffer)  
  2.         throws IOException {  
  3.   
  4.     int count = 0;  
  5.     int maxCount = 20;  
  6.   
  7.     while (byteBuffer.hasRemaining()) {  
  8.         int len = socketChannel.write(byteBuffer);  
  9.         if (len < 0) {  
  10.             throw new EOFException("write channel is closed.");  
  11.         }  
  12.         // 如果不对len==0(即当前网络不可用)的情况处理,则while(byteBuffer.hasRemaining())可能一直  
  13.         // 循环下去而消耗大量的CPU.  
  14.         if (len == 0) {  
  15.             count++;  
  16.         } else {  
  17.             count = 0;  
  18.         }  
  19.         if (count > maxCount) {  
  20.             throw new IOException("can't connect to target.");  
  21.         }  
  22.     }  
  23.   

分享到:
评论

相关推荐

    core-nio:nio底层实现原理(另外还有aio的功能)

    主要有以下几个概念Channel(渠道,类似于高速公路可以处理很多线程io);Selector(选择器,可以理解为是一个监听者,可以绑定多个渠道);Buffer(缓冲区,读写数据时的一块临时区域,本质上是一块可以存储数据的...

    JAVA3D的网络三维技术的设计与实现.rar

    设计与实现一个基于Java 3D的网络三维技术涉及以下几个关键方面: 网络通信模块设计:首先需要设计一个网络通信模块,用于实现客户端和服务器之间的通信。可以使用Java的Socket或者Java NIO等网络编程技术来实现...

    深入浅出NETTY

    构,底层都需要一个...由于 Netty 源码的复杂性和 NIO 编程本身的技术门槛限制,对于大多数初学者而言,通过自己阅读和分析源码来深入掌握 Netty 的设计原理和实现细节是件非常困难的事情。感兴趣的朋友可以过来看看

    基础深化和提高-网络编程

    Java的网络编程通常涉及以下几个方面: Socket编程:基于Socket套接字,通过TCP或UDP协议进行网络通信。通过创建Socket对象,程序可以在网络上进行数据传输和通信。 URL和URLConnection:Java提供了java.net.URL和...

    高清_书签_Netty进阶之路 跟着案例学Netty.zip

    在本书中,作者将在过去几年实践中遇到的问题,以及Netty学习者咨询的相关问题,进行了归纳和总结,以问题案例做牵引,通过对案例进行剖析,讲解问题背后的原理,并结合Netty源码分析,让读者能够真正掌握Netty,在...

    Netty 超高清

    第一部分详细地介绍Netty的相关概念以及核心组件,第二部分介绍自定义协议经常用到的编解码器,第三部分介绍Netty对于应用层高级协议的支持,会覆盖常见的协议及其在实践中的应用,第四部分是几个案例研究。...

    java面试笔试题库java学习笔记开发教程互联网公司面试资料大全合集.zip

    关于Java框架Vert.x的几点思考.docx 关于堆和栈的那些事.docx 写好Java代码的30条经验总结.docx 华为java笔试面试题2014.doc 多态的理解.docx 大公司最喜欢问的Java集合类面试题.docx 大公司的Java面试题集.doc 就业...

    java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB.zip

    关于Java框架Vert.x的几点思考.docx 关于堆和栈的那些事.docx 写好Java代码的30条经验总结.docx 华为java笔试面试题2014.doc 多态的理解.docx 大公司最喜欢问的Java集合类面试题.docx 大公司的Java面试题集.doc 就业...

    java8集合源码分析-geektime-java-week-training-camp:极客时间-Java每周训练营

    相关工具以及面临复杂问题时的几个高级工具的使用; JVM 问题排查分析的常用手段、性能调优的最佳实践经验等; JVM 相关的常见面试问题必知必会、全面分析。 第二周:NIO 技术 - 构建高吞吐服务器的终极武器 同步/...

    百度地图开发java源码-inertiaSearch:挑战赛

    时间不长,导致几个关键的想法没有落实(同类index 分散存储 VS 集中存储;按照题意如何减少io次数),最终成绩为33名。但是因为程序倾注了个人很多的心血,技术实现甚至能够超过排名靠前的队伍,并且有很多借鉴的地方...

    积分管理系统java源码-dunwu.github.io:个人文档整理项目

    领域的知识点非常庞杂,我整理成了多个教程项目,每个项目都包含大量文档和代码。 :books: - 本人作为一名 Java 程序员,十年的技术积累,汇总与此。 :books: - Java 核心技术教程。内容包含:Java 语法基础特性、...

    activej:ActiveJ是从头开始构建的替代Java平台。 ActiveJ重新定义了Java中的Web,高负载和云编程,具有最终的性能和可伸缩性!

    ActiveJ由几个模块组成,这些模块在逻辑上可以分为以下类别: Async.io-具有高效事件循环,NIO,promise,流和CSP的高性能异步IO。 替代Netty,RxJava,Akka等。 ( , , , ,) HTTP-具有WebSocket支持的高性能...

    xmljava系统源码-spark-notes:在编写spark或scala时注意任何事情

    Java内存分为哪几个区域?相互之间是如何转化地? Java 1.7和1.8有什么改变呢? HashMap和HashTable有什么区别? 请说一下你对Java关键字 synchronized 的理解,如果你不懂Java语言的话,也请尝试说说你对 ...

    java开源包1

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包11

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包2

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包3

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包6

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包5

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    java开源包10

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

Global site tag (gtag.js) - Google Analytics