ChannelHandler是处理业务逻辑的代码所在。下面首先分析下ChannelHandler的体系结构,然后重点分析几个有意思的Handler:IdleStateHandler、ExceptionHandler
1.ChannelHandler在Netty中的位置
ChannelHandler分upstreamChannelHandler和downstreamChannelHandler,分别处理upstream事件和downstream事件。下图是一个Channel组成示意图。
Channel通过ChannelFactory创建,pipeline通过PipelineFactory创建,默认每个Pipeline都有独立的一系列Handler,每当生成pipeline时都会创建Handler。HandlerContext对Handler进行包装,相互之间形成链式结构,依次传递事件进行处理。upstream链处理由网络层向用户传递事件,包括对消息的解码decoder,用户对事件的响应方法;downstream链处理由用户向网络层发送事件,包括写事件、消息编码,最后由sink接收处理downstream事件操作SocketChannel。
2.ChannelUpstreamHandler
该类定义了对upstream事件的处理接口,而SimpleChannelUpstreamHandler是对事件的简单实现adapter,如果用户自己实现其对消息的处理,可直接继续SimpleChannelUpstreamHandler,重写相应的方法,如messageReceived、channelOpen等。
3.FrameDecoder
因为TCP流在传输时会分片、重组,并不能断定字节流中的一个数据包的结束。如发送方的数据可能是这样分段
* +-----+-----+-----+
* | ABC | DEF | GHI |
* +-----+-----+-----+
因为数据包的分片,接收方的数据可能就是:
* +----+-------+---+---+
* | AB | CDEFG | H | I |
* +----+-------+---+---+
因此需要decoder对字节流进行分段,由字节序列转成有正确、意义的POJO。
decoder作为SimpleChannelUpstreamHandler的子类,其实也是一个Handler,只不过不是直接处理业务逻辑,而对根据规定的网络协议进行解码分段。因此解码过程自然在messageReceived进行。
if (cumulation == null) { try { // the cumulation buffer is not created yet so just pass the input to callDecode(...) method callDecode(ctx, e.getChannel(), input, e.getRemoteAddress()); } finally { updateCumulation(ctx, input); } } else { input = appendToCumulation(input); try { callDecode(ctx, e.getChannel(), input, e.getRemoteAddress()); } finally { updateCumulation(ctx, input); } }
在解码时,如果上次还有残留未处理的buffer,则在appendToCumulation中和这次收到的数据合并成一个新的buffer进行解码。在callDecode中,当数据足够解码出一个POJO时,则继续发送upstream事件,只是这次的消息对象由ChannelBuffer变成了解码的POJO。如果解码还有数据剩余,甚至一个POJO都没有解码出来,则需要在updateCumulation中将这些数据保存在decoder中,以便下次收到消息时再合并处理。
对于使用者来说,构建自己协议的decoder,需要继承FrameDecoder,重写decode()方法,当解码出POJO时则返回该对象,否则返回NULL并重置buffer的readIndex等待下次解码。
如
public class BigIntegerDecoder extends FrameDecoder { @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { // Wait until the length prefix is available. if (buffer.readableBytes() < 5) { return null; } buffer.markReaderIndex(); // Check the magic number. int magicNumber = buffer.readUnsignedByte(); if (magicNumber != 'F') { buffer.resetReaderIndex(); throw new CorruptedFrameException( "Invalid magic number: " + magicNumber); } // Wait until the whole data is available. int dataLength = buffer.readInt(); if (buffer.readableBytes() < dataLength) { buffer.resetReaderIndex(); return null; } // Convert the received data into a new BigInteger. byte[] decoded = new byte[dataLength]; buffer.readBytes(decoded); return new BigInteger(decoded); } }
因此FrameDecoder更像是从流里面捡出一个个消息体StreamToOneDecoder
4.OneToOneDecoder
这是一个消息转成另一个消息的解码器,常常在FrameDecoder解码之后再使用,因为经过FrameDecoder解码后的一个个消息已经是完整独立的消息对象,所以它不需要处理消息分隔的工作,它所承担的职责也就轻很多,例如Base64编码等工作。对使用者来说,只要继承它并重写decode方法。
5.ChannelDownstreamHandler
与ChannelUpstreamHandler相对应,只是在事件向相反方向传递时对事件进行响应,包括对writeRequested、connectRequested等处理。
使用者可以实现接口ChannelDownstreamHandler的handleDownstream来实现自己的逻辑,或者直接继续重写SimpleChannelDownstreamHandler的各事件的处理方法。
6.OneToOneEncoder
相对decoder来说,encoder需要做的工作并不多,因为在发消息时并没有多个消息界限不明的问题,因此并不需要有相对的FrameEncoder。使用者只需继承它并重写encode方法。
7.SimpleChannelHandler
这个Handler同时继承了ChannelUpstreamHandler、ChannelDownstreamHandler,将upstream downstream两种事件的处理逻辑集中在一个类中。
8.ExecutionHandler
在不使用这个Handler进行处理事件时,其它Handler的事件处理是同步进行的,即一个Worker线程在收到消息后执行业务Handler的方法,如果方法内有非常耗时或者阻塞的操作如DB访问,这时Worker线程就不能及时返回处理其它请求,对性能影响较高。而ExecutionHandler就是为这种情况设计了,它提供了一种异步处理任务的机制,将它之后handler处理以任务的形式投递到线程池中并直接返回。
public void handleUpstream( ChannelHandlerContext context, ChannelEvent e) throws Exception { if (handleUpstream) { executor.execute(new ChannelUpstreamEventRunnable(context, e, executor)); } else { context.sendUpstream(e); } }
ExecutionHandler不像其它Handler都是独立的,它是所有Handler共享使用。其使用OrderedMemoryAwareThreadPoolExecutor线程池来保证同一个Channel上事件的先后顺序。
9.IdleStateHandler
这是Netty提供的超时检测处理Handler,可分别设置读超时、写超时、读写超时的时间。Netty采用Hashed Wheel模式设计定时器,将这些IdleStateHandler放到定时器中。每当有消息事件经过时及时更新时间,如果超时则自它向上传递超时事件。注意这个超时是人为设计的,并非socket真正在超时,此时仍能对socket读写。对使用者来说,如果要使用这个Handler,则业务逻辑处理类须继续IdleStateAwareChannelUpstreamHandler,重写其channelIdle方法。如果要保持长连接可以此处发送心跳包。
相关推荐
《Netty源码深入分析》是由美团基础架构部的闪电侠老师所分享的一系列关于Netty源码解析的视频教程。以下将根据标题、描述、标签以及部分内容等信息,对Netty及其源码进行深入剖析。 ### Netty简介 Netty是基于...
Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。在深入探讨 Netty 源码之前...通过分析源码,不仅可以提升自己的技术能力,还能为解决实际问题提供灵感和参考。
在本地化修改场景下,你可以通过阅读源码理解Netty如何处理各种网络事件,如何优化性能,甚至可以根据项目需求自定义Handler或解码器。例如,如果你需要支持一种新的协议,可以参考已有的协议处理器来编写新的编码器...
在本篇Netty4.0学习笔记中,我们将聚焦于如何在实际应用中混合使用`coder`和`handler`,这是Netty框架中非常关键的一部分,对于构建高性能、低延迟的网络应用程序至关重要。Netty是一个用Java编写的异步事件驱动的...
通过分析和学习Netty 4.1的源码,我们可以深入理解其设计理念,如如何实现高效的并发处理,如何优雅地处理I/O事件,以及如何设计灵活的网络协议处理器。这对于开发高性能网络应用,特别是分布式系统和微服务架构,...
在本篇Netty源码教程的第四部分,我们将深入探讨Netty框架的核心组件和工作原理,以便更好地理解和利用这个高性能、异步事件驱动的网络应用框架。Netty被广泛应用于分布式系统、高并发服务器和复杂网络协议的实现,...
5. **Pipeline**:ChannelPipeline是Netty的处理链,包含一系列ChannelHandler,每个Handler负责特定的处理任务,可以方便地添加、删除和调整Handler。 6. **线程模型**:Netty使用EventLoopGroup来管理EventLoop,...
四、Netty源码分析 1. Channel初始化:探讨ServerBootstrap和Bootstrap如何创建并初始化Channel。 2. 事件传播:分析EventLoop如何处理和分发事件,以及Handler如何接收和处理这些事件。 3. ByteBuf实现:研究...
Netty在Android开发中的应用实战系列(二)——— Encoder | Decoder | Handler 的使用:https://azhon.blog.csdn.net/article/details/100831777 Netty在Android开发中的应用实战系列(三)——— 心跳处理 | 断线...
源代码分析有助于开发者更有效地利用Netty的特性,解决在实际项目中遇到的问题,或者根据需求定制特定功能。 Netty的核心概念包括: 1. **Channel**:它是网络连接的抽象,可以代表TCP连接、UDP套接字或任何其他...
Netty的解码器(Decoder)和编码器(Encoder)是实现网络通信的关键组件。在这个示例中,可能包含自定义的编解码器来处理特定的数据格式。例如,如果数据是以特定的协议(如自定义的时间戳协议)传输,那么解码器会...
- **Decoder与Encoder**:Netty提供了一系列预定义的编解码器,如LineBasedFrameDecoder、LengthFieldBasedFrameDecoder等,用于处理不同格式的数据。开发者可以自定义编解码器,将特定的数据格式转换为可处理的...
在源码中,`src`目录下的代码涵盖了Netty的所有组件,包括`channel`、`handler`、`buffer`、`pipeline`等关键模块。以下是一些主要知识点: 1. **Channel**:这是Netty中的基本概念,代表了网络连接。每个Channel都...
Netty是一个高性能、异步事件驱动的网络应用程序框架,它非常适合用来构建WebSocket服务器。 Netty提供了WebSocketServerProtocolHandler来处理WebSocket协议,但在默认情况下,它会对接收到的数据包大小进行限制。...
这个压缩包“netty入门加实战全栈项目之仿微信聊天源码.zip”显然包含了使用Netty实现的一个仿微信聊天应用的源代码,这为我们提供了一个很好的学习和实践Netty的机会。 首先,让我们深入理解Netty的核心特性: 1....
- **Decoder** 和 **Encoder**:用于数据的解码和编码,比如将接收到的字节流转换为业务对象,或将业务对象转换为字节流发送。 - **Pipeline**:每个Channel都有一个处理链,包含多个Handler,按照顺序处理I/O事件。...
- **Pipeline(管道)**: Pipeline是消息在Channel中的传输路径,由多个处理器(Handler)组成,每个处理器负责特定的业务逻辑。 2. **特性** - **高度可定制化**: Netty允许用户自定义Channel、EventLoop、...
2. **高度可定制的管道(Pipeline)**:Netty的事件处理机制通过一系列处理器(Handler)组成的管道实现,每个处理器执行特定的任务,如解码、编码、业务逻辑处理等。用户可以根据需求自由组合和扩展处理器链。 3. ...
例如,Decoder用于解码接收到的数据,Encoder用于编码要发送的数据。 6. **Bootstrap(引导类)**:用于初始化和配置服务器或客户端。通过设置ChannelFactory、Group、Pipeline等,然后调用bind或connect方法启动...
Pipeline是一系列处理器(Handler)的链,每个处理器处理特定的事件或数据;EventLoopGroup则是负责事件处理的线程池。 在本项目中,我们需要关注如何在Netty 3中实现JT809协议的解析和编码。JT809协议规定了消息头...