引问:NIO在服务端的应用已经被广为熟悉,但是在客户端的使用,其实给予的指导并不多。同时在我看来,NIO在客户端使用就是原来的长连接模式加上事件驱动的框架,而相对于短连接池模式来说,性能是否真的在任何环境都那么突出,其实不然。
最近正好要优化TB的Cache客户端,原始代码是用NIO写的,但是效率不高,性能也一般,因此反而拖累了服务端的表现,在整个优化过程中,看了NIO2,也就是JDK7中比较突出的AIO,同时也经过反复优化和测试,其中对于NIO应用到客户端来谈一下自己的一些收获。
传统IO操作和NIO操作的区别
简单来说:1.对于数据处理由Stream方式转变称为了Block方式。2.事件驱动机制替换了传统的一个线程处理到底的模式。
第一种转变不适合需要对于字节流做处理的场景。(需要对字节充分作处理,例如我在另一个优化中对于字节流采用lazy Analysis,通过边解析边交验的方式,提前过滤无效请求,降低由于分析大数据包无效请求带来的性能消耗)。但是Block传输和处理这种转变符合操作系统的真实模式,使Java可以充分利用各个操作系统的实现来优化性能,同时管道的思想也符合操作系统的真实实现(原来Java都是将双向通道拆分为In,Out的)。事件驱动使得完整的处理流程被拆分成为流水线作业,最大程度上利用了资源,防止后端处理成为了前端请求的瓶颈,降低了服务器的吞吐量,同时最大限度的给开发者优化流程,缩短关键路径的机会。
下面表格大致列举了传统IO和NIO在客户端使用的需求和各自的优势(两者都需要的就不列入其中了,例如容错恢复等)
|
需求 |
优势 |
IO(连接池) |
1. 连接池的管理。2. 高并发大压力下Socket数量庞大,对于文件句柄消耗也大 |
1. 数据发送接受处理简单,单线程模式。2.可以对字节流逐一解析,避免内存过分无谓消耗 |
NIO |
1. 交互的协议需要支持会话。(也可以不支持,这就会使得处理模式退化,效率下降,后续会谈到)2. 对于接收和发送需要支持多线程,提高效率3. 需要对Channel和Block有所熟悉 |
1. 资源利用率最大化,性能提升(消息通道的充分利用,操作系统IO优化的使用)2. 充分灵活的将处理分割为多个工作项,流水线作业,减小业务处理对服务器服务请求接收吞吐量的影响。 |
NIO在客户端
上图描述的是NIO实现的客户端和服务端之间的数据交互场景,可以看到由于要最大限度利用消息通道,NIO客户端需要具备以下几点:
1.消息会话支持。
2.多线程访问控制。
3.消息过滤容错。
4.超时控制。
5…..
消息会话支持,指的是消息的传输与接收需要通过通信协议方式来实现会话,NIO在服务端可以很容易使用就是因为,NIO Server自身维护了服务处理会话,而对于客户端来说,首先上图可以看到,不同的线程使用NIO Client的时候,发送消息后得到回复的顺序并不一定和消息发送的顺序一致,因此需要通过在协议中内嵌会话码,这样才能够在结果返回并解析以后通知消息接收者。
多线程访问控制,对于消息的发送和接收,可以通过单线程处理,但是这将成为高并发下的处理瓶颈(后续的优化过程会详细说明)。如果允许多线程发送和接收消息,那么对于发送队列和接收缓存都将存在着并发访问控制的需求,同时对于发送和接收在数据需要切分的时候,需要通过小事务来保证数据的一致性。
消息过滤容错,由于共享一个数据通道,当返回数据出现问题的时候,如何过滤出错的数据防止解析异常和死锁很重要,也是多线程处理不相互影响的保证。
超时控制,由于共享数据通道是异步服务接收模式,因此请求队列会成为在高并发下的消耗资源,必要的将队列中的超时请求删除,是在网络异常或者服务端异常的时候,不会被压垮的保证。
其他其实还有很多设计细节和要点,这里就不一一列出了。
归结起来,如何高效的协调好多线程数据发送和接收及分析是共享数据通道的NIO客户端最重要的实现关注点。
NIO客户端优化分析
在优化前,客户端的结构就是最早的传统的NIO模式,以下两张图就可以说明大致的结构。
第一张图是NIO的概念模型,第二张图则是从实际使用的角度去描述了NIO框架中各个角色在实际模型中所处的地位。具体的NIO角色工作介绍就不详细说了,这里描述一下问题,在高并发下,发现等待消息返回的线程池内的线程不断堆积,同时处理性能也不断下降,呈现为恶性循环的状态。
传统模式下,事件处理是单线程串行处理的,例如如果ReadEvent在WriteEvent之前,当ReadEvent出现服务超时或者本身就比较耗时,那么导致其他事件无法得到处理或者处理比较慢。
初步认定原因:串行化事件处理,导致事件之间处理效率将会相互影响。
考虑的解决方式:事件处理线程化,将事件触发与事件处理分离,提高不同事件处理能力。
从上图中看到Dispatcher调用Handler处理事件和事件监听已经剥离开来,这样应该看起来会很好的解决当前的问题。
三下五除二,在Dispatcher中新增加了一个Executor(没有采用Cache的,使用的是fix的,防止资源由于暴涨导致产生大量线程搞垮应用),然后将attachment中的Channel代理类作了线程安全改造。
测试结果让我大跌眼镜,在高并发下不仅没有提高效率,反而效率降低。仔细观察后发现,由于对于连接,读,写三种事件都做了注册,在高并发下,读写的事件频度很高,因此会产生大量的线程,同时线程池是fix的(如果是Cache就直接OOM了),在创建线程中消耗的远大于原来认为是瓶颈的顺序执行,加上对于Channel代理的线程安全改造(资源锁等安全优化),导致最终性能还不抵原来的初始架构。
看来在Dispatcher中通过线程池方式来剥离框架和业务逻辑并不合适,因此考虑从其他角度入手。从另一方面来考虑,其实如果能够做到将Read事件和Write事件内部处理作的足够轻量,就算是顺序处理也会有很好的效果,因此就有了下面的设计:
LightWeightHander是一个轻量级的消息处理Handler,对于原来的业务数据处理做了拆分,达到就算是串行事件处理也能够防止事件之间相互影响。
工作拆分实际上成了优化最重要的部分,下面描述了工作拆分的分析点(也是不断通过测试结果得出的最后的策略)
上图是长连接模式中最常见的方式,系统中存在着发送和接收的缓冲区,但是对于发送没有采用多线程处理,而仅仅采用批量处理(设定一定的阀值,在高并发下缓冲区内容突然增长则采用批量Flush的方式提高效率),写出不采用多线程其实也是经过测试发现性能在线程创建过程中会有损耗,而且写的动作消耗时间有限,不需要做此优化。但是对于读部分,则存在着很多数据分析,拷贝,对象反序列化等操作,因此需要切割工作,支持并行处理,提高反馈速度。这里主要将读取分析过程切分成三部分,前两部分都是单线程完成操作,最后一步交由多线程池来执行。
第一步是由LightWeightHandler通过读取事件了解有数据需要读入,然后将数据包分别读入挂接在读入队列中。此处不使用多线程有两个原因,第一个原因也是线程创建消耗问题,第二个原因是当数据包由于过大需要分包接收,此时采用多线程接收,则会导致消息顺序错乱(消息包根据设定的接收窗口大小读入,因此没有编排)。第二步是将读入的数据包按照报文协议规则划分逻辑单元(此处需要采用ByteBuffer的各种特性,尽量避免创建新的数据块和相互拷贝,提高效率)。这部分采用单线程原因是因为此处无法实行流水线作业,本身就是串行的工作,关键路径无法缩短。第三步,将逻辑切分后的数据报文分配给每一个线程执行最耗时的解析和回调工作。
最后测试效果还是不错的,总结出来的几点经验:
1.多线程消耗有时候超过你的瓶颈,同时还需要防范在高并发下资源申请的问题。
2.谨慎分割的任务,将任务处理串行和并行结合起来,找到最短路径。
3.NIO处理中充分利用ByteBuffer等Buffer,数据逻辑分段和隔离,尽量少用拷贝和新建方式,提高4.处理速度。(需要注意的是Buffer的相对操作函数使用方式,避免出现Buffer复用导致异常)
批量处理需要做好测试,找到合理的阀值,防止批量带来的延时效应和峰值效应。
5.多线程调试尽量多用打印输出运行期状态了解性能瓶颈。(调试就不必了,基本无法确切了解内部高速运转细节)
NIO2
最近也关注了JDK7中的很多信特性,当然NIO2不得不说,这里还是提一下和本文比较相关的AIO。在NIO2中正式的出现了异步IO的接口及实现,对于这种异步模式,框架提供了两种方式获取结果:
采用Future的方式来获取。
采用Callback的方式(CompletionHandler)来获取。
在上面优化的框架中也有两种机制的实现,第二种就直接将方法注册到key的attachment中。第一种的实现如下图:
就是通过会话码及对象的Wait和Notify来实现的。
下图就是AIO的结构图:
其实AIO是在NIO的几个版本结构更新以后增加了更好的异步回调封装,异步回调封装就不多说了,这里就谈谈NIO迭代的几个版本中新增加的几个角色。
ChannelFacade其实就和优化过程中使用长连接最通用的输入输出缓存设计一样,对Channel外部在作了一层封装,提供优化和拦截处理的入口。InputHandler也就是负责对于输入内容的一个逻辑分析,判断是否需要Flush,这和优化中的批量Flush也十分类似。
后话
总的看来其实NIO在客户端的应用和我起初的想法还是比较一致,就是长连接模式加上事件驱动框架,同时如何处理好多线程并发控制及任务切分才是体现出NIO信道复用的关键,否则还是简单的用客户端连接池来的省心。
分享到:
相关推荐
NIO(Non-blocking Input/Output,非阻塞输入/输出)是Java提供的一个高效、灵活的I/O模型,尤其适用于高并发、...通过实践和分析这个示例代码,我们可以更好地掌握异步I/O、事件驱动编程以及Netty提供的各种高级功能。
Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从1.4...通过深入理解并实践这个示例,开发者可以更好地掌握Java NIO的核心概念和技术,并将其应用于实际项目中,提升系统的性能和可扩展性。
【标题】:“NIOServer”是...通过阅读和分析“NIOServer.java”,开发者可以深入理解Java NIO的工作原理,以及如何利用NIO实现高效的网络服务器。同时,这个项目也可以作为参考,帮助开发者构建自己的网络应用或服务。
标题中的“网络与nio”指的是Java的非阻塞I/O(Non-blocking I/O)特性,主要涉及Java NIO(New IO)库。Java NIO在Java 1.4版本引入,为开发者提供...通过分析和运行这些代码,我们可以更好地掌握Java NIO的实践技巧。
《NIO入门》一书是理解Java NIO(New Input/Output)的重要参考资料,NIO在Java编程中扮演着至关重要的角色,特别是在处理高并发、大数据...结合源码分析和实践应用,能更深入地理解和运用NIO机制,提升Java编程技能。
在Java编程领域,NIO...通过以上分析,我们可以看到,基于NIO的聊天室利用了Java NIO的高效特性,实现了多客户端并发连接,提升了系统的整体性能。这个项目对于理解和掌握Java NIO的概念和实战具有很好的实践价值。
`NonBlockingServer.java`和`Client.java`的代码分析和实践,可以帮助我们更好地理解和掌握Java NIO在Socket通信中的应用。在实际开发中,我们还需要考虑异常处理、资源管理等细节,以确保系统的稳定性和可靠性。
#### 四、NIO实践中的常见问题及解决方案 - **CPU占用率过高**:如果注册了不感兴趣的事件,可能会导致CPU占用率异常升高。解决办法是确保只注册真正感兴趣的事件类型。 - **避免线程阻塞**:在NIO中,单个线程需要...
本篇文章将深入探讨NIO在连网和异步IO方面的应用,以及如何通过源码理解和实践相关技术。 首先,我们了解NIO的核心概念。NIO不同于传统的IO模型(即BIO,Blocking IO),它引入了通道(Channel)和缓冲区(Buffer)...
该教程分为两个主要部分,分别针对理论分析和实战演练,确保学员能够从理论到实践全方位掌握相关知识。 第一部分:深入浅出Netty源码剖析 这部分教程主要讲解Netty框架的内部工作机制,包括其设计模式、事件驱动...
它不仅涉及到网络编程的基础知识,如套接字通信,还涵盖了多线程、并发控制以及高效数据传输的技巧,是学习和理解Java NIO体系结构的一个很好的实践案例。通过深入研究和分析这个项目,开发者能够更好地掌握NIO在...
在提供的文档"tcp nio 服务端、客户端例子--参考《分布式Java应用:基础与实践》.doc"中,可能会包含具体的NIO服务端和客户端的代码示例,这些示例将展示如何使用`ServerSocketChannel`、`SocketChannel`、`Selector...
总结来说,本教程将引导你从理论到实践,掌握Java NIO的基本原理,理解Mina框架的使用,以及如何在SpringBoot环境中整合Mina实现高效的网络通信。通过这些知识的学习,你将具备开发高并发、高性能网络应用的能力。
总的来说,这个项目为学习FTP协议、Java网络编程和客户端/服务器交互提供了一个实践平台。通过分析和修改这些代码,开发者可以深入理解FTP的工作流程,同时提升Java编程技能,尤其是处理网络通信和多线程的部分。...
在提供的学习资料中,“JAVA_NIO(全面细致).pdf”和“Java_NIO_细节.pdf”可能涵盖了NIO的基本概念、API使用、实例分析等内容,帮助深入理解NIO的原理和实践。而“nio原理与实例(看).png”和“提升网管通讯模块...
3. **案例分析**:通过具体的案例分析,让学员理解如何在实际开发中应用NIO解决特定问题。 4. **性能优化**:探讨如何利用NIO进行性能优化,比如合理配置缓冲区大小、优化多线程模型等。 5. **实战项目**:提供完整...
标题中的“3种下载文件程序的思考,为何使用NIO进行异步网络通讯”提示了我们探讨的主题,即网络通信中的不同下载策略以及为何选择非阻塞I/O...学习和理解NIO的原理和实践,对于提升我们的IT专业技能是非常有价值的。
通过以上分析,我们可以看到Java NIO在TCP编程中的强大之处,以及如何通过`EchoProtocol`接口和`TCPEchoSelectorProtocol`类实现一个简单的回显服务器。在实际应用中,这种模型可以扩展到更复杂的网络服务,例如聊天...
mina框架是Apache组织开发的一个网络通信框架,它基于Java NIO(非阻塞I/O)构建,用于简化网络编程,尤其是TCP和UDP协议的应用开发。本项目提供了服务端和客户端的示例,使得开发者能够更好地理解和应用MINA框架。 ...