1.Netty源码探究1:事件驱动原理
2.Nettyåç-ä»NIOå¼å§
3.Java的并行世界-Netty中线程模型源码讲解-续集Handler、Channel
4.Java编程方法论-Reactor-Netty与Spring WebFlux解读 之 第4章 Reactor Netty针对EventLoopGroup的封装
5.实现一款高可用的 TCP 数据传输服务器(Java版)
6.让我们一起探索一下Netty(Java)底层的“零拷贝Zero-Copy”技术
Netty源码探究1:事件驱动原理
Netty源码探究1:事件驱动原理
Netty借鉴了Reactor设计模式,这是一种事件处理模式,用于管理并发服务请求。在模式中,服务处理器对请求进行I/O多路复用,源码什么作用并同步分发给相应的请求处理器。Netty的核心内容是Reactor,因此深入分析其在Netty中的应用至关重要。Netty吸收了前人优秀经验,构建出这款优秀的技术框架。
在Reactor设计模式中,Demultiplexer和Dispatcher是关键概念。Netty中的Demultiplexer是如何实现的?答案在于其Server端的架构设计。Netty通过Bootstrap(ServerBootstrap也适用)来构建Server,其中bind方法是启动Reactor运行的关键。在bind方法中,Netty创建并注册Channel到EventLoopGroup,从而实现Demultiplexer的功能。
Netty中的Channel与JDK中的Channel有何不同?Netty通过NioServerSocketChannel构建Server,其内部封装了Java NIO的Channel,但Netty的Channel与JDK中的Channel在注册到Selector时有所不同。Netty中的Channel注册到NioEventLoop中的Selector上,只关注OP_ACCEPT事件。当客户端连接时,事件被触发,Server响应客户端连接。这涉及NioServerSocketChannel的构造过程和Selector的创建。
Dispatcher在Java NIO中负责事件分发,Netty中如何实现这一功能?在NioEventLoop中,更新模块源码Selector.select()方法配合run()函数,共同实现事件监听循环。run函数中包含事件状态机和事件分派逻辑。当有事件到来时,状态机触发processSelectedKeys()方法,根据事件类型调用相应处理器进行处理。
Netty中的事件驱动原理最终如何与自定义handler关联?在NioEventLoop的processSelectedKey()方法中,事件处理逻辑与Channel.Unsafe接口相关联。Channel.Unsafe接口用于封装Socket的最终操作,Netty通过此接口与业务层Handler建立关联。通过调用handler的read方法,Netty将事件与业务处理逻辑关联起来。
总之,Netty通过Reactor设计模式实现了事件驱动原理,借助Demultiplexer和Dispatcher的机制,实现了对并发请求的高效处理。理解Netty的源码结构和事件驱动原理,对于深入掌握Netty技术框架至关重要。
Nettyåç-ä»NIOå¼å§
Nettyæ¯åºäºNIOçå¼æ¥éä¿¡æ¡æ¶ï¼æ¾ç»å¼å ¥è¿AIOï¼åæ¥æ¾å¼ï¼ï¼æ è¦è¯´Nettyåçæ们è¦å ä»NIOå¼å§ã
NIO æ¯JAVAå¨JDK4ä¸å¼å ¥çåæ¥éé»å¡é信模åï¼å¨NIOåºç°ä¹åï¼JDK4ä¹åï¼å¸åºä¸åªæä¸ä¸ªBIO模å顾åæä¹BLOCKING IO ï¼åæ¥é»å¡é信模åï¼
BIOï¼BLOCKING I/Oï¼ï¼
BIO 为ä¸ä¸ªè¿æ¥ ä¸ä¸ªçº¿ç¨ç模å¼ï¼å½æè¿æ¥æ¶æå¡å¨ä¼å¼å¯ä¸ä¸ªçº¿ç¨æ¥å¤ç请æ±
è¥æ¤è¯·æ±å¥é½ä¸æ³å¹²æ¤æ¶çº¿ç¨ä¼æä¹æ ·ï¼
æ¤çº¿ç¨ä¼è¿å ¥é»å¡æ¨¡å¼ï¼BLOCKINGï¼ï¼---å¥ä¹ä¸å¹²ï¼å¹²ççzzZZ~
è¿ç§ä¸è¿æ¥ï¼ä¸çº¿ç¨ç模å¼ä¼é ææå¡å¨èµæºä¸å¿ è¦çå¼é并ä¸å¨å¤§éè¿æ¥è®¿é®æ¶ æå¡å¨ä¼åçä»ä¹ï¼è½¦éï¼çº¿ç¨ï¼ä¸è¶³ï¼è½¦å¤ªå¤--æå µè½¦äº
ç±æ¤å°±åºç°äºNIO
â
NIOï¼new/NONBLOCKING I/Oï¼:
NIO为åæ¥éé»å¡é信模åï¼Selectï¼å¤è·¯å¤ç¨å¨ï¼ä¸ºæ¤æ¨¡åçæ ¸å¿ï¼å®ç°äºå¤ä¸ªè¿æ¥ä¸ä¸ªçº¿ç¨
å½æ客æ·ç«¯è¿æ¥è¯·æ±æ¶ æ¤è¿æ¥è¯·æ±ä¼è¢«æ³¨åè³selectä¸ï¼å½selectæ£æµå°æ¤è¿æ¥æI/O请æ±æ¶æä¼æå¼ä¸ä¸ªçº¿ç¨å»å¯¹æ¤I/O请æ±è¿è¡å¤ç-----å线ç¨æ¨¡å
è¿ä¸ªæ¶åæ人é®äºï¼è¿ä¹å¤æä½é½å¨ä¸ä¸ªçº¿ç¨éï¼çº¿ç¨å¿ä¸è¿æ¥æä¹åï¼
æ¤æ¶ ç±äºç½ç»è¯·æ±ãI/O读åãä¸å¡æä½é½å¨ä¸ä¸ªçº¿ç¨ä¸ï¼ä¼å¯¼è´å¨é«å¹¶åçæ åµä¸åå¨æ§è½ç¶é¢ äºæ¯ä¹æ人就æåºæ¥ å°ä¸å¡æä½ä¸¢å°å¦ä¸ä¸ªçº¿ç¨æä¹æ ·ï¼
äºæ¯åºç°äºç¬¬ä¸ç§reactor模å-使ç¨çº¿ç¨æ± è¿è¡æä½ç½ç»è¯·æ±ãIOå¨ä¸ä¸ªçº¿ç¨ï¼ä¸å¡æä½å¨å¦ä¸ªä¸ä¸ªçº¿ç¨ çä¸å¡å离----线ç¨æ± 模å
ä»æ¤å¾ä¸å¯ä»¥çåºæ¤æ¶ 模åä¸ä½¿ç¨ä¸ä¸ªçº¿ç¨æ± æ¥è¿è¡ç½ç»è¯·æ±ãIO读å
å½è¯»åå®æåå°ä¸å¡æä½ç»å®å¨çº¿ç¨æ± ä¸å¦å¤ç线ç¨ä¸-------ç½ç»IOä¸ä¸å¡æä½å¯ä»¥åæ¥è¿è¡äºï¼ä¸åé½å®ç¾äºèµ·æ¥ï¼
ä½æ¯ï¼äºæ è¿æ²¡å®ï¼ï¼è¿ä¸ªæ¶ååæ人æåºé®é¢ï¼å¨é«å¹¶åçæ¶åååï¼ä¼ä¸ä¼ææ§è½ç¶é¢
å 为ç½ç»IOæ¯é常æ¶èCPUçï¼å½ç½ç»è¯·æ±ä¸ç½ç»IOå¨å个线ç¨ä¸æ¶ï¼é CKçæ åµä¸å个线ç¨å¹¶ä¸è¶³ä»¥æ¯æèµ·ææçIOæä½ï¼å æ¤ä¹å½¢æäºå¨é«å¹¶åç¶æä¸çæ§è½ç¶é¢
äºæ¯å¤§ä½¬ä»¬å°±æ³çï¼å¦ææIOæåºæ¥è®©å个线ç¨æ± å»æ¥æ¶ç½ç»è¯·æ±ï¼ç¨å¦ä¸ä¸ªçº¿ç¨æ± æ¥è¿è¡IOä¸ä¸å¡æä½ä¼ä¸ä¼æ´å¥½
äºæ¯ç¬¬åç§Reactor模ååºè¿èç--主ä»Reactorå¤çº¿ç¨æ¨¡å
æ¤æ¨¡åä¸ mainReactoråªç¨äºæ¥æ¶ç½ç»è¯·æ±ï¼èsubReactorä¸ä¸ºä¸ä¸ªçº¿ç¨æ± ï¼çº¿ç¨æ± ä¸æ¯ä¸ªçº¿ç¨ä¸ç»å®ä¸ä¸ªselect
å½mainReactoræ¥æ¶å°è¯·æ±æ¶ï¼ä¸ä¸ªæè¿°ç¬¦ï¼ ç³»ç»ä¼çæä¸ä¸ªæ°çæ述符代表æ¤è¿æ¥çæï¼æ¤æ¶mainReactorä¼å°æ°çæ述符éè¿ä¸ä¸ªç®æ³å¨çº¿ç¨æ± ä¸éå®ä¸ä¸ªçº¿ç¨ å°æ¤æ述符ç»å®è³æ¤çº¿ç¨æ± ä¸çselectä¸ï¼ç±æ¤çº¿ç¨æ¥å¯¹è¯·æ±è¿è¡I/O ä¸ä¸å¡æä½
ä»æ¤ç¾ä¸è¿æ¥é«å¹¶åä¸æ¯é®é¢
åå°è¿ æ们æ¯ä¸æ¯æ³èµ·äºNettyçå¯å¨è¿ç¨
1ã声æ两个EventLoopGroupä¸ä¸ªä¸ºbossï¼mainReactorï¼ä¸ä¸ªä¸ºworkerï¼subReactorï¼
EventLoopGroupï¼çº¿ç¨æ± ï¼åå§åçæ¶åä¼çæï¼æå è½½ï¼æå®æ°éçEventLoopï¼çº¿ç¨ï¼è¥æ æå® åä¼çæCPUæ°X2ç线ç¨
2ã声æä¸ä¸ªå¯å¨è¾ å©ç±»Bootstrap并å°EventLoopGroup注åå°å¯å¨è¾ å©ç±»BootStrapä¸(bootStrap.group)
æ¥çåç»bootstrapæå®channel模åçå±æ§ï¼åæ·»å ä¸ä¸å¡æµæ°´çº¿ï¼channelpipelineï¼å¹¶ä¸å¨pipelineä¸æ·»å ä¸ä¸å¡æä½handlerï¼ï¼éè¿channelpipelineå¯ä»¥å¯¹ä¼ å ¥æ°æ®ä¸ºæ欲为ï¼
3ãç»å®ç«¯å£
Nettyå¯å¨å®æ
è¿æ¶åå¯è½æ人ä¼é®äºï¼è¿åä½ ä¸é¢è¯´çreactorï¼NIOæå¥å ³ç³»ï¼
è¿ä¸ªæ¶åæ们è¦è¿ä¹ç
â
è¥æ们å°bossä¸worker线ç¨æ± 设置为ç¸åçä¸ä¸ªçº¿ç¨æ± ï¼é£ä¹ä¼åçä»ä¹äºï¼
æ¤æ¶å ³æ³¨ä¸ä¸ç¬¬ä¸ä¸ªReactor模åæ¶å°±ä¼åç° å½BOSS=WORKERæ¶å nettyå®ç°çå°±æ¯ç¬¬ä¸ç§Reactor模å 使ç¨çº¿ç¨æ± 模å
èå½bossä¸çäºworkerçæ¶å使ç¨çå°±æ¯ç¬¬åç§ ä¸»ä»å¤çº¿ç¨æ¨¡å
Nettyå°±æ¯åºäºReactor模åæ¥å¯¹NIOè¿è¡äºæç¨åå°è£ ï¼ä»Nettyæºç ä¸å°±å¯ä»¥çåºæ¥å ¶å®åºå±è¿é½æ¯NIOçæ¥å£
æ¤æ¬¡å¤ä¸ºèªå·±è¯»æºç ä¹åçç解 å¦æ误请ææ£
ææ©
åææ¿ä¸ç¬¬ä¸ä¸ªèµ
Java的并行世界-Netty中线程模型源码讲解-续集Handler、Channel
Netty 的核心组件 ChannelHandler 在网络应用中扮演着处理入站和出站事件及数据的关键角色。ChannelHandler 的子类负责执行不同类型的事件处理和数据操作,以实现特定的网络业务逻辑。以下是 ChannelHandler 子类的分类及其功能介绍:
首先,特殊类型的Handler,如 ChannelHandlerContext,它连接了处理器与Channel之间的上下文关系,方便数据交互和事件触发。
其次,ChannelInboundHandler 和 ChannelOutboundHandler 分别负责处理入站和出站的jdk论坛源码数据。ChannelInboundHandlerAdapter 示例如时间服务器,当连接建立时发送时间并断开,而 ChannelOutboundHandlerAdapter 则如客户端发送消息。
ByteToMessageDecoder 和 MessageToByteEncoder 分别负责数据的解码和编码,如基于换行符的文本协议服务器和字符串消息的编码。
ChannelDuplexHandler 如聊天服务器,处理双向通信,例如广播消息。SimpleChannelInboundHandler 提供了便捷的入站事件处理,避免了手动管理消息引用计数。
Channel相关的核心概念是 Channel,它代表了网络连接,隐藏了底层通信方式的细节,支持数据读写和事件监听。Netty 提供了多种Channel子类,如 NioServerSocketChannel 和 EpollServerSocketChannel,用于适应不同应用场景。
在服务器启动时,ChannelInitializer 用于初始化新连接的 ChannelPipeline,配置处理器以执行特定的业务逻辑。Netty 4.1 源码结构提供了学习的入口,后续会分享更详细的注释版源码。
总的来说,通过理解和使用这些 ChannelHandler 和 Channel 的特性,开发者可以构建出功能丰富的网络应用。持续关注,将分享更多源码解析和学习资源。
Java编程方法论-Reactor-Netty与Spring WebFlux解读 之 第4章 Reactor Netty针对EventLoopGroup的封装
Java编程方法论-Reactor-Netty与Spring WebFlux解读整体简介第4章 Reactor Netty针对EventLoopGroup的封装
在功能使用层面,为对应如HttpServer、servlet源码中文TcpServer等类中的Create方法,Reactor Netty设计了EventLoopGroup的Create方法,以适应响应式环境,实现功能增强。此章节将深入解析这一过程。
针对EventLoopGroup的接口设计采用装饰器模式,引入reactor.netty.resources.LoopResources接口,它继承自Disposable接口,EventLoopGroup作为其实现类的一部分。默认的Create方法返回此接口的默认实现。
在顶层接口内,设置了默认方法,用于获取客户端或服务端的EventLoopGroup。对Netty中的workerGroup和bossGroup进行设定。确认EventLoopGroup是否可关闭,并提供了相应的daemon方法。
EventLoopGroup继承EventExecutorGroup,关闭操作通过调用EventExecutorGroup的shutdownGracefully方法实现,包装为Mono.defer,订阅执行关闭逻辑。顶层接口中定义了disposeLater方法,用于封装这一逻辑,实现类中重写。
获取EventLoopGroup与对应的ServerChannel,提供onServerChannel方法进行确认。通过preferNative方法判断本地是否支持Epoll与KQueue,实现层面判断功能支持,使用Class.forName进行判断,异常表示不支持。激光测距 源码
针对操作系统支持的EventLoopGroup,DefaultLoop接口提供获取实例、确认支持的Channel类型和名称获取方法。实现中通过静态代码块进行逻辑判断,选择对应类型,实现懒汉模式。
LoopResources接口的create方法返回默认的EventLoopGroup实例,考虑多核处理和缓存策略,使用AtomicReference管理实例。针对selectCount的策略设定,实现bossGroup与workerGroup共享或独立。
获取EventLoopGroup实例的逻辑分为本地支持情况下的默认实现和不支持时的通用NioEventLoopGroup获取。cacheNativeServerLoops和cacheNioServerLoops分别处理本地支持和不支持情况。
通过拓展ThreadFactory设定线程名称,结合原子计数获取EventLoopGroup线程数,实现更清晰的线程属性描述。Reactor Netty的Dispose逻辑通过Mono.defer封装,确保所有资源释放完毕后结束订阅。
本章学习到Reactor Netty针对EventLoopGroup的封装设计,通过上层接口提供核心功能,隐藏实现细节,融合Reactor与Netty,了解原子类、ThreadFactory等技术的用法,以及封装、缓存策略等实践。
实现一款高可用的 TCP 数据传输服务器(Java版)
实现一款高可用的TCP数据传输服务器,可以利用Netty这款高性能、封装性良好且灵活的开源框架。Netty能够用于手写web服务器、TCP服务器等,支持丰富协议,如HTTP、HTTPS、WEBSOCKET,提供大量方法,可根据需求定制服务器。
使用Netty编写TCP服务器或客户端时,可以自由设计数据传输协议、自定义编码规则和解码规则、处理socket流攻击、TCP粘包和拆包问题。
创建普通Maven项目,无需依赖第三方web服务器,通过main方法执行。加入POM依赖,设计基于TCP的数据传输协议。使用进制表示协议的开始位(0x)和结束位(0x),用字节进行表示。
TCP服务器启动类需要使用bootstrap绑定工作线程组、channel类以及自定义pipeline中的handler类。注意,自定义handler的添加顺序决定了数据流动路径:底层字节流的解码/编码处理器、业务处理处理器。
编码器负责按照协议格式将数据发送给客户端,实现继承MessageToByteEncoder。解码器为核心部分,自定义解码协议、处理粘包和拆包问题,实现继承ByteToMessageDecoder。
解决粘包问题时,正常数据传输为完整数据包,但在接收到的数据中可能存在多个数据包的情况。Netty默认将二进制字节码放入byteBuf中,因此需要按照协议设计原则处理粘包问题,解析协议、数据字节、结束标志,并将数据放入out列表中。
拆包问题同样由ByteToMessageDecoder解决。在解决拆包问题时,需等待不完整数据的剩余部分,将已收到的数据与后续数据合并后进行解码。Netty设计原则是:当读取到的长度超过byteBuf可读内容时,表示发生拆包,调用resetReaderIndex复位读操作指针并结束decode方法。
业务处理handler类处理完整的解析数据,通过进一步反序列化对象,避免在反序列化时需要处理多种对象类型的情况。使用DTObject包装数据,避免每增加一种对象类型时的if判断。
编写TCP客户端进行测试。启动类的init方法定义客户端handler,模拟连接建立后向服务端发送数据,使用channel的write方法发送数据。配置编码器,将对象自动转换成字节码放入bytebuf中。
进行正常数据传输测试,结果成功解析出实体对象。在debug模式下输出数据抓包展示,数据转换为字节码以二进制形式传输,以进制显示,包括开始标志、长度、数据内容和结束标志。
模拟粘包问题,注释编码器,发送多帧数据封装在单个包中,修改EchoHandler。服务器成功解析出三帧数据,BusinessHandler的channelRead方法被调用多次。抓包显示数据被黏合在同一个包中。
模拟拆包问题,再次注释编码器,修改EchoHandler。客户端将数据分割成两包发送,服务端在第一包数据时终止解码,并在channelRead中等待第二包数据,将两包数据合并再次解码。
同时出现拆包、粘包场景时,注释编码器,修改EchoHandler。查看服务端输出结果,验证Netty成功处理了数据的拆包与粘包问题。
总结:通过遵循Netty的设计原则,可以轻松解决TCP数据传输中的拆包、粘包问题。尽管使用DTObject包装数据避免了处理多种对象类型时的if判断,但仍然无法处理传输List、Map等复杂数据结构的情况。
让我们一起探索一下Netty(Java)底层的“零拷贝Zero-Copy”技术
Netty中的零拷贝技术主要体现在其底层对操作系统零拷贝策略的应用以及在ByteBuf实现上的优化。零拷贝指的是数据传输过程中,数据不需要经过CPU的拷贝操作,直接在用户空间与内核空间之间传输。传统的零拷贝实现方式需经历四次数据拷贝和四次上下文切换,Netty通过使用Java的FileChannel.transferTo方法避免了不必要的数据拷贝步骤,达到了零拷贝效果。在操作系统的层面,Netty实现了零拷贝,而在ByteBuf层面,Netty进一步提供了零拷贝的实现。
对于ByteBuffer,Netty提供了两个接口:Direct Buffers和堆外内存。Direct Buffers允许在内存区域直接分配空间,无需在堆内存中分配,从而减少数据在JVM堆内存与堆外内存之间的不必要的拷贝。堆外内存则允许在JVM内部执行I/O操作时,数据直接在堆外内存中操作,避免了数据从堆内存到堆外内存的移动,实现了零拷贝。
在ByteBuf实现上,Netty提供了多种优化。例如,对于传统的ByteBuffer,如果需要将两个ByteBuffer中的数据组合到一起,则需要创建新的数组并进行数据拷贝。然而,Netty提供的组合ByteBuf(Composite ByteBuf)实现了零拷贝,它保存了多个Buffer的引用而非实际组合,避免了数据的物理移动。此外,Netty在I/O操作中使用了FileChannel的transferTo方法,该方法依赖于操作系统实现零拷贝,进一步优化了数据传输效率。
Netty的零拷贝技术总结起来主要体现在操作系统级别的零拷贝、ByteBuffer的Direct Buffers和堆外内存应用以及ByteBuf的实现优化。这些技术共同作用于减少数据拷贝和上下文切换,提高系统性能和效率。在堆外内存的回收方面,Netty依赖于JVM的垃圾回收机制,但在实际应用中,开发者需要主动调用System.gc()来触发内存回收,以避免DirectByteBuffer对象及关联的堆外内存占用过多资源。合理管理DirectByteBuffer对象及其堆外内存的使用,可以有效避免内存泄漏和资源浪费,确保系统稳定运行。