文章目录
netty版本
- netty版本:
io.netty:netty-all:4.1.33.Final
Channel
-
Channel代表一个到实体(如一个硬件设备、一个文件、一个Socket或者一个能够执行一个或者多个不同的IO操作的程序组件)的开放连接,如读和写操作。Channel是对Socket的抽象,Channel接口提供的API,大大的降低了Socket类的复杂性 -
可以把
Channel看作是传入或者传出数据的载体,Channel可以被打开或者被关闭,连接或者断开连接 -
使用java原生API进行网络编程,当你想从阻塞传输切换到非阻塞传输,这个切换不是那么容易的,反之亦然。Netty在它的传输协议实现上提供了统一的API,使得这种解决方案更简单。传输API的核心是
channel接口,用于所有的outbound操作.

-
从上图中可以看到,每个
Channel都将会被分配一个ChannelPipeline和ChannelConfig。ChannelConfig包含了该Channel的所有配置设置,并且支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个ChannelConfig的子类型 -
Channel重要方法方法名称 描述 eventLoop()返回分配给 Channel的EventLooppipeline()返回分配给 Channel的ChannelPipelineisActive()如果 Channel是活动的,则返回 true。活动的意义可能依赖于底层的传输。例如,一个Socket传输一旦连接到了远程节点便是活动的,而一个Datagram传输一旦被打开便是活动的localAddress()返回绑定到本地的SocketAddress remoteAddress()返回绑定到远程的SocketAddress write()将数据写到远程节点。这个数据将被传递给 ChannelPipeline, 并且排队直到它被flushflush将之前已写的数据冲刷到底层传输,如一个Socket writeAndFlush一个简便的方法,等同于调用 write() 并接着调用flush() -
Netty中
Channel的特性Channel是独一无二的Channel是线程安全的
Channel生命周期
-
Channel的生命周期状态状态 描述 ChannelUnregisteredChannel已创建,还未注册到一个EventLoop上ChannelRegisteredChannel已经注册到一个EventLoop上ChannelActiveChannel是活跃状态(连接到某个远端),可以收发数据ChannelInactiveChannel未连接到远端
-
当
Channel状态发生变化时,将会生成对应的事件,这些事件会被转发给ChannelPipeline中的ChannelHandler
ChannelHandler
ChannelHandler不仅仅充当所有处理入站和出站数据的应用程序逻辑的容器,而且还可以处理其他的动作,比如将数据从一种格式转换为另外一种格式,或者处理转换过程中所抛出的异常等。ChannelHandler典型的用法- 将数据从一种格式转换为另一种格式
- 提供异常的通知
- 提供
Channel变为活动的或者非活动的通知 - 提供当
Channel注册到EventLoop或者从EventLoop注销时的通知; - 提供有关用户自定义事件的通知
ChannelHandler生命周期
-
ChannelHandler生命周期类型 描述 handlerAdded当把 ChannelHandler添加到ChannelPipeline中时被调用handlerRemoved当从 ChannelPipeline中移除ChannelHandler时被调用exceptionCaught当处理过程中在 ChannelPipeline中有错误产生时被调用 -
ChannelHandler接口方法public interface ChannelHandler { void handlerAdded(ChannelHandlerContext ctx) throws Exception; void handlerRemoved(ChannelHandlerContext ctx) throws Exception; void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception; @Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Sharable { // no value } } -
Netty定义了两个重要的
ChannelHandler子接口ChannelInboundHandler:处理入站数据以及各种状态变化ChannelOutboundHandler:处理出站数据并且允许拦截所有的操作
ChannelInboundHandler
-
方法
类型 描述 channelRegistered当 Channel已经注册到它的EventLoop并且能够处理I/O时被调用channelUnregistered当 Channel从它的EventLoop注销并且无法处理任何I/O才被调用channelActive当 Channel处于活动状态时被调用,Channel已经连接/绑定并且已经就绪channelInactive当 Channel离开活动状态并且不再连接它的远程节点时被调用channelReadComplete当 Channel上的一个读操作完成时被调用,可能在channelReadComplete()被调用之前看到多次调用channelRead()channelRead当从 Channel读取数据时被调用,当某个ChannelInboundHandler的实现重写了此方法时,需要手动显示的释放与池化ByteBuf实例相关的内存,Netty提供了ReferenceCountUtil.release(msg)来手动释放ChannelWritabilityChanged当 Channel的可写状态发生改变时被调用。 用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。可以通过调用Channel的isWritable()方法来检测Channel的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWaterMark()方法来设置userEventTriggeredChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个POJO被传经了ChannelPipeline -
常规的运行顺序
//连接成功 handlerAdded() channelActive() //客户端发送数据过来 channelRead() channelReadComplete() //连接失效 channelInactive() channelUnregistered handlerRemoved()
ChannelOutboundHandler
-
出站操作和数据将由
ChannelOutboundHandler处理。它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用。 -
ChannelOutboundHandler的一个强大的功能是可以按需推迟操作或者事件,这使得可以通过一些复杂的方法来处理请求。例如:如果到远程节点的写入被暂停了,那么你可以推迟冲刷操作井在稍后继续。 -
ChannelOutboundHandler接口的方法方法 描述 bind(ChannelHandlerContext, SocketAddress , ChannelPromise) 当请求将 Channel 绑定到本地地址时被调用 connect(ChannelHandlerContext , SocketAddress , SocketAddress , ChannelPromise) 当请求将 Channel 连接到远程节点时被调用 disconnect(ChannelHandlerContext, ChannelPromise) 当请求将 Channel 从远程节点断开时被调用 close(ChannelHandlerContext ctx, ChannelPromise promise) 当请求关闭 Channel 时被调用 deregister(ChannelHandlerContext ctx, ChannelPromise promise) 当请求将Channel从它的EventLoop注销时被调用 read(ChannelHandlerContext) 当请求从 Channel 读取更多的数据时被调用 flush(ChannelHandlerContext) 当请求通过 Channel 将入队数据冲刷到远程节点时被调用 write(ChannelHandlerContext,Object, ChannelPromise) 当请求通过 Channel将数据写到远程节点时被调用 -
ChannelPromise与ChannelFutureChannelOutboundHandler 中的大部分方法都需要一个ChannelPromise参数,以便在操作完成时得到通知。ChannelPromise是ChannelFuture的一个子类,其定义了一些可写的方法,如setSuccess()和setFailure(),从而使ChannelFuture不可变(当一个Promise被完成之后,其对应的Future的值便不能再进行任何修改了)
ChannelHandler适配器
-
ChannelinboundHandlerAdapter和ChannelOutboundHandlerAdapter分别提供了ChannelinboundHandler与ChannelOutboundHandler的基本实现

-
ChannelHandlerAdapter还提供了实用方法isSharable()。 如果其对应的实现被标注为@Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline。当收集跨域多个Channel的统计信息时,我们可能需要共享通过一个ChannelHandler
资源管理
-
当通过调用
ChannelinboundHandler.channelRead()或者ChannelOutboundHandler.write()方法来处理数据时,都需要确保没有任何的资源泄露。Netty使用引用计数来处理池化的ByteBuf,所以在完全使用完ByteBuf后,调整其引用计数很重要。 -
为了帮助诊断潜在的资源泄露问题,Netty提供了
ResourceLeakDetector,它将对你应用程序的缓冲区分配做大约 1%的采样来检测内存泄露,相关的开销是非常小的。如果检测到内存泄露,会调用reportUntracedLeak方法打印错误日志protected void reportUntracedLeak(String resourceType) { logger.error("LEAK: {}.release() was not called before it's garbage-collected. " + "Enable advanced leak reporting to find out where the leak occurred. " + "To enable advanced leak reporting, " + "specify the JVM option '-D{}={}' or call {}.setLevel() " + "See http://netty.io/wiki/reference-counted-objects.html for more information.", resourceType, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), simpleClassName(this)); } -
Netty定义了集中检测级别
ResourceLeakDetector.Level。级别 描述 DISABLED 禁用泄漏检测。只有在详尽的测试之后才应设置为这个值 SIMPLE 使用 1%的默认采样率检测并报告任何发现的泄露。这是默认级别,适合绝大部分的情况 ADVANCED 使用默认的采样率,报告所发现的任何的泄露以及对应的消息被访问的位置 PARANOID 类似于 ADVANCED,但是其将会对每次(对消息的)访问都进行来样。这对性能将会有很大的影响,应该只在调试阶段使用 -
内存泄露检测级别可以通过java系统属性来定义
java -Dio.netty.leakDetection.level=ADVANCED或者java -Dio.netty.leakDetectionLevel=ADVANCED,对应ResourceLeakDetector类的属性。如果设置新的就以新的为准//老的 private static final String PROP_LEVEL_OLD = "io.netty.leakDetectionLevel"; //新的 private static final String PROP_LEVEL = "io.netty.leakDetection.level"; // First read old property name String levelStr = SystemPropertyUtil.get(PROP_LEVEL_OLD, defaultLevel.name()); // If new property name is present, use it levelStr = SystemPropertyUtil.get(PROP_LEVEL, levelStr); -
如果不需要入站数据在
channelRead()中直接消费而不需要转发到下一个ChannelInboundHandler,可以继承SimpleChannelinboundHandler并覆写channelRead0()方法自动释放消息,因为SimpleChannelInboundHandler自动释放资源,任何对消息的引用都会变成无效,所以你不能保存这些引用待后来使用. -
对于出站数据,如果处理了
writer()操作并丢弃一个消息,那么应该负责释放它,并且通知ChannelPromise数据已经被处理了(否则可能会出现ChannelFutureListener收不到某个消息已经被处理了的通知的情况)。public void write(ChannelHandlerContext ctx,Object msg,ChannelPromise promise){ ReferenceCountUtil. release (msg); promise.setSuccess(); }
ChannelPipeline
- Netty允许自定义
ChannelHandler的实现来处理数据。ChannelPipeline是ChannelHandler实例的列表,用于处理或截获Channel的接收和发送数据。ChannelPipeline提供了一种高级的截取过滤器模式,让用户可以在ChannelPipeline中完全控制一个事件及如何处理ChannelHandler与ChannelPipeline的交互 - 当
Channel被创建时,它会被自动地分配到它专属的ChannelPipeline。一旦连接,Channel和ChannelPipeline的耦合性是永久的,Channel不能附加其他的ChannelPipeline或从ChannelPipeline分离。 每个Channel都有一个属于自己的ChannelPipeline,调用Channel#pipeline()方法可以获得Channel的ChannelPipeline,调用Pipeline#channel()方法可以获得ChannelPipeline的Channel。 - 很明显,
ChannelPipeline里面就是一个ChannelHandler的列表。ChannelHandler的执行顺序是由添加顺序决定的。如果一个入站IO事件被触发,这个事件会从第一个开始依次通过ChannelPipeline中的ChannelHandler。若是一个出站I/O事件,则会从最后一个开始依次通过ChannelPipeline中的ChannelHandler。ChannelHandler可以处理事件并检查类型,如果某个ChannelHandler不能处理则会跳过,并将事件传递到下一个ChannelHandler。ChannelPipeline可以动态添加、删除、替换其中的ChannelHandler,这样的机制可以提高灵活性。

- 总结
ChannelPipeline保存了与Channel相关联的ChannelHandler;ChannelPipeline可以根据需要,通过添加或者删除ChannelHandler来动态地修改。例如当业务高峰期需要对系统做拥塞保护时,根据时间判断,动态地将系统拥塞保护ChannelHandler添加到当前的ChannelPipeline中。当高峰期过去之后,可以动态删除ChannelPipeline有着丰富的API用以被调用,以响应入站和出站事件- 入站调用顺序与
ChannelInboundHandler添加顺序一样,出站调用顺序与ChannelOutboundHandler添加顺序相反 ChannelPipeline是线程安全的
ChannelHandlerContext
-
ChannelHandlerContext使得ChannelHandler能够和它的ChannelPipeline以及其他的ChannelHandler交互。ChannelHandler可以通知其所属的ChannelPipeline中的下一个ChannelHandler,甚至可以动态修改它所属的ChannelPipeline中的ChannelHandler的编排(顺序) -
每当有
ChannelHandler添加到ChannelPipeline中时,都会创建ChannelHandlerContext。ChannelHandlerContext有很多与Channel和ChannelPipeline类似的方法,如果调用Channel或ChannelPipeline这些方法它们将沿着整个ChannelPipeline进行传播,而调用位于ChannelHandlerContext上相同的方法,则将从当前所关联的ChannelHandler开始,并且只会传播给位于该ChannelPipeline中的下一个能够处理该事件的ChannelHandler -
ChannelHandlerContext、ChannelPipeline与ChannelHandler关系图



-
方法
方法 描述 alloc 返回和这个实例相关联的 Channel 所配置的 ByteBufAllocator bind 绑定到给定的 SocketAddress,并返回 ChannelFuture channel 返回绑定到这个实例的 Channel close 关闭 Channel,并返回 ChannelFuture connect 连接给定的 SocketAddress,并返回 ChannelFuture deregister 从之前分配的 EventExecutor 注销,并返回 ChannelFuture disconnect 从远程节点断开,并返回 ChannelFuture executor 返回调度事件的 EventExecutor fireChannelActive 触发对下一个 ChannelInboundHandler 上的 channelActive()方法(己连接)的调用 fireChannelinactive 触发对下一个 ChannelInboundHandler 上的 channelInactive()方法(己关闭〉的调用 fireChannelRead 触发对下一个 ChannelInboundHandler 上的 channelRead() 方法(己接收的消息)的调用 fireChannelReadComplete 触发对下一个 ChannelInboundHandler 上的 channelReadComplete ()方法的调用 fireChannelRegistered 触发对下 一个 ChannelInboundHandler 上的fireChannelRegistered ()方法的调用 fireChannelUnregistered 触发对下 一个 ChannelInboundHandler 上的 fireChannelUnregistered ()方法的调用 fireChannelWritabilityChanged 触发对下 一个 ChannelInboundHandler 上的 fireChannelWritabilityChanged ()方法的调用 fireExceptionCaught 触发对下 一个 ChannelInboundHandler 上的 fireExceptionCaught(Throwable)方法的调用 fireUserEventTriggered 触发对下 一个 ChannelInboundHandler 上的 fireUserEventTriggered(Object evt)方法的调用 handler 返回绑定到这个实例的 ChannelHandler isRemoved 如果所关联的 ChannelHandler 已经被从 ChannelPipeline 中移除则返回 true name 返回这个实例的唯一名称 pipeline 返回这个实例所关联的 ChannelPipeline read 将数据从 Channel 读取到第一个入站缓冲区;如果读取成功则触发一个 channelRead事件,并(在最后一个消息被读取完成后)通知 ChannelInboundHandler 的 channelReadComplete(ChannelHandlerContext)方法 write 通过这个实例写入消息并经过 ChannelPipeline writeAndFlush 通过这个实例写入并冲刷消息并经过 ChannelPipeline -
虽然
ChannelHandlerContext可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。
修改ChannelPipeline
-
ChannelHandler可以通过添加、删除或者替换其他的ChannelHandler来实时地修改ChannelPipeline的布局(它也可以将它自己从ChannelPipeline中移除)方法 描述 addFirst 添加ChannelHandler在ChannelPipeline的第一个位置 addBefore 在ChannelPipeline中指定的ChannelHandler名称之前添加ChannelHandler addAfter 在ChannelPipeline中指定的ChannelHandler名称之后添加ChannelHandler addLast 在ChannelPipeline的末尾添加ChannelHandler remove 删除ChannelPipeline中指定的ChannelHandler replace 替换ChannelPipeline中指定的ChannelHandler -
通常
ChannelPipeline中的每一个ChannelHandler都是通过它的EventLoop(I/O线程)来处理传递给它的事件的。所以不要阻塞这个线程,因为这会对整体I/O处理产生负面的影响。有的时候可能需要与那些使用阻塞API的遗留代码进行交互,比如JDBC。对于这种情况,ChannelPipeline有一些接受一个EventExecutorGroup的add()方法。如果一个事件被传递给一个自定义的EventExecutorGroup,它将被包含在这个EventExecutorGroup中的某个EventExecutor所处理,从而被从该Channel本身的EventLoop中移除。对于这种,Netty提供了默认实现DefaultEventExecutorGroup -
ChannelPipeline用于访问Channelhandler的方法方法 描述 get 通过类型或者名称返回ChannelHandler context 返回和ChannelHandler绑定的ChannelHandlerContext names 返回ChannelPipeline中所有的Channelhandler的名称
ChannelPipeline触发事件
-
入站操作
方法 描述 fireChannelRegistered 调用ChannelPipeline中下一个 ChannelInboundHandler的channelRegistered(ChannelHandlerContext)方法 fireChannelUnregistered 调用ChannelPipeline 中下一个 ChannelInboundHandler的channelUnregistered(ChannelHandlerContext)方法 fireChannelActive 调用ChannelPipeline中下一个ChannelInboundHandler 的 channelActive (ChannelHandlerContext)方法 fireChannelInactive 调用 ChannelPipeline 中下一个 ChannelInboundHandler 的 channelInactive(ChannelHandlerContext )方法 fireExceptionCaught 调用 ChannelPipeline中下一个 ChannelInboundHandler的exceptionCaught(ChannelHandlerContext, Throwable)方法 fireUserEventTriggered 调用ChannelPipeline中下一个 ChannelInboundHandler的userEventTriggered(ChannelHandlerContext, Object)方法 fireChannelRead 调用ChannelPipeline中下一个ChannelInboundHandler 的 channelRead (ChannelHandlerContext, Object msg)方法 fireChannelReadComplete 调用 ChannelPipeline 中下一个 ChannelInboundHandler 的channelReadComplete(ChannelHandlerContext)方法 fireChannelWritabilityChanged 调用 ChannelPipeline 中下一个 ChannelInboundHandler的channelWritabilityChanged(ChannelHandlerCon.text)方法 -
出站操作
方法 描述 bind 将Channel绑定到一个本地地址,这将调用 ChannelPipeline 中的下一个 ChannelOutboundHandler 的 bind(ChannelHandlerContext, SocketAddress , ChannelPromise)方法 connect 将Channel连接到一个远程地址,这将调用 ChannelPipeline 中的下一个 ChannelOutboundHandler的connect(ChannelHandlerContext,SocketAddress, ChannelPromise)方法 disconnect 将Channel断开连接。这将调用ChannelPipeline中的下一个ChannelOutboundHandler的disconnect (ChannelHandlerContext,ChannelPromise)方法 close 将 Channel 关闭 。这将调用 ChannelPipeline 中的下一个 ChannelOutboundHandler 的 close(ChannelHandlerContext, ChannelPromise)方法 deregister 将 Channel 从它先前所分配的 EventExecutor (即 EventLoop)中注销。这将调用 ChannelPipeline中的下一个ChannelOutboundHandler的deregister(ChannelHandlerContext,ChannelPromise)方法 flush 冲刷 Channel所有挂起的写入。这将调用 ChannelPipeline 中的下一个 ChannelOutboundHandler 的 flush(ChannelHandlerContext)方法 write 将消息写入Channel。这将调用 ChannelPipeline中的下一个ChannelOutboundHandler的write(ChannelHandlerContext , Object msg , ChannelPromise)方法。注意:这并不会将消息写入底层的 Socket, 而只会将它放入队列中。 要将它写入 Socket,需要调用 flush ()或者 writeAndFlush()方法 writeAndFlush 这是一个先调用 write ()方法再接着调用 flush ()方法的便利方法 read 请求从 Channel 中读取更多的数据。这将调用 ChannelPipeline 中的下一个 ChannelOutboundHandler的read(ChannelHandlerContext)方法 -
从性能角度考虑,为了防止频繁唤醒Selector进行消息发送,Netty的write()方法并不直接将消息写入SocketChannel中,调用write()方法只是把待发送的消息放到发送缓冲数组中,再通过flush方法,将发送缓冲区中的消息全部写到SocketChannel中。
本文详细介绍了Netty框架的核心概念,包括Channel、ChannelHandler及其生命周期,ChannelPipeline的工作原理,以及如何管理和修改ChannelPipeline。深入探讨了ChannelHandlerContext的作用,以及如何通过ChannelPipeline触发事件。
2859

被折叠的 条评论
为什么被折叠?



