生命周期是由Channel定义的,一共有四种生命周期,如下图所示
状态 | 描述 |
---|---|
ChannelUnregistered | Channel 已经被创建,但还未注册到EventLoop |
ChannelRegistered | Channel 已经被注册到了EventLoop |
ChannelActive | Channel 处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了 |
ChannelInactive | Channel 没有连接到远程节点 |
interface ChannelHandler 定义的生命周期操作,在ChannelHandler被添加到ChannelPipeline 中或者被从ChannelPipeline 中移除时会调用这些操作。这些方法中的每一个都接受一个ChannelHandlerContext 参数。对应的方法如下图所示:
类型 | 描述 |
---|---|
handlerAdded | 当把ChannelHandler 添加到ChannelPipeline 中时被调用 |
handlerRemoved | 当从ChannelPipeline 中移除ChannelHandler 时被调用 |
exceptionCaught | 当处理过程中在ChannelPipeline 中有错误产生时被调用 |
Netty定义了两个重要的ChannelHandler子接口:ChannelInboundHandler(处理入站数据已经各种状态的格式化),ChannelOutBoundHandler(处理出站数据并且允许拦截所有的操作)。
如下列出了ChannelInboundHandler接口中的方法:
方法 | 描述 |
---|---|
channelRegistered | 当Channel 已经注册到它的EventLoop 并且能够处理I/O 时被调用 |
channelUnregistered | 当Channel 从它的EventLoop 注销并且无法处理任何I/O 时被调用 |
channelActive | 当Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪 |
channelInactive | 当Channel 离开活动状态并且不再连接它的远程节点时被调用 |
channelReadComplete | 当Channel上的一个读操作完成时被调用① |
channelRead | 当从Channel 读取数据时被调用ChannelWritability-Changed当Channel 的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel 变为再次可写时恢复写入。可以通过调用Channel 的isWritable()方法来检测Channel 的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法来设置 |
userEventTriggered | 当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个POJO 被传经了ChannelPipeline |
注意:当某个ChannelInboundHandler 的实现重写channelRead()方法时,它将负责显式地释放与池化的ByteBuf 实例相关的内存。Netty 为此提供了一个实用方法ReferenceCount-Util.release().
出站操作和数据将由ChannelOutboundHandler 处理。它的方法将被Channel、Channel-Pipeline 以及ChannelHandlerContext 调用。
ChannelOutboundHandler方法如下所示:
方法 | 描述 |
---|---|
bind(ChannelHandlerContext,SocketAddress,ChannelPromise) | 请求将Channel 绑定到本地地址时被调用 |
connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise) | 当请求将Channel 连接到远程节点时被调用 |
disconnect(ChannelHandlerContext,ChannelPromise) | 当请求将Channel 从远程节点断开时被调用 |
close(ChannelHandlerContext,ChannelPromise) | 当请求关闭Channel 时被调用 |
deregister(ChannelHandlerContext,ChannelPromise) | 当请求将Channel 从它的EventLoop 注销时被调用 |
read(ChannelHandlerContext) | 当请求从Channel 读取更多的数据时被调用 |
flush(ChannelHandlerContext) | 当请求通过Channel 将入队数据冲刷到远程节点时被调用 |
write(ChannelHandlerContext,Object,ChannelPromise) | 当请求通过Channel 将数据写到远程节点时被调用 |
你可以使用ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter类作为自己的ChannelHandler 的起始点。这两个适配器分别提供了ChannelInboundHandler和ChannelOutboundHandler 的基本实现。通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口ChannelHandler 的方法。
每一个新创建的Channel 都将会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel 既不能附加另外一个ChannelPipeline,也不能分离其当前的。在Netty 组件的生命周期中,这是一项固定的操作,不需要开发人员的任何干预。并且ChannelHandler之间的传递是通过ChannelHandlerContext实现的。
ChannelHandlerContext使得ChannelHandler能够和它的ChannelPipeline以及其他的ChannelHandler 交互。ChannelHandler 可以通知其所属的ChannelPipeline 中的下一个ChannelHandler,甚至可以动态修改它所属的ChannelPipeline。ChannelHandlerContext 具有丰富的用于处理事件和执行I/O 操作的API。
ChannelHandler 可以通过添加、删除或者替换其他的ChannelHandler 来实时地修改ChannelPipeline 的布局。
下面列出了ChannelHandler 的用于修改ChannelPipeline 的方法:
方法 | 描述 |
---|---|
addAfteraddLast | 将一个ChannelHandler 添加到ChannelPipeline 中 |
remove | 将一个ChannelHandler 从ChannelPipeline 中移除 |
replace | 将ChannelPipeline 中的一个ChannelHandler 替换为另一个Channel-Handler |
方法 | 描述 |
---|---|
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)方法 |
fireChannelWritability-Changed | 调用ChannelPipeline 中下一个ChannelInboundHandler 的channelWritabilityChanged(ChannelHandlerContext)方法 |
方法 | 描述 |
---|---|
bind | 将Channel 绑定到一个本地地址,这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的bind(ChannelHandlerContext, Socket-Address, ChannelPromise)方法 |
connect | 将Channel 连接到一个远程地址,这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的connect(ChannelHandlerContext, Socket-Address, ChannelPromise)方法 |
disconnect | 将Channel 断开连接。这将调用ChannelPipeline 中的下一个ChannelOutbound-Handler 的disconnect(ChannelHandlerContext, Channel Promise)方法 |
close | 将Channel 关闭。这将调用ChannelPipeline 中的下一个ChannelOutbound-的close(ChannelHandlerContext, ChannelPromise)方法 |
deregister | 将Channel 从它先前所分配的EventExecutor(即EventLoop)中注销。这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的deregister(ChannelHandlerContext, ChannelPromise)方法 |
flush | 冲刷Channel所有挂起的写入。这将调用ChannelPipeline 中的下一个Channel-OutboundHandler 的flush(ChannelHandlerContext)方法 |
write | 将消息写入Channel。这将调用ChannelPipeline 中的下一个Channel-OutboundHandler的write(ChannelHandlerContext, Object msg, Channel-Promise)方法。注意:这并不会将消息写入底层的Socket,而只会将它放入队列中。要将它写入Socket,需要调用flush()或者writeAndFlush()方法 |
writeAndFlush | 这是一个先调用write()方法再接着调用flush()方法的便利方法 |
read | 请求从Channel 中读取更多的数据。这将调用ChannelPipeline 中的下一个ChannelOutboundHandler 的read(ChannelHandlerContext)方法 |
ChannelHandlerContext 代表了ChannelHandler 和ChannelPipeline 之间的关联,每当有ChannelHandler 添加到ChannelPipeline 中时,都会创建ChannelHandler-Context。ChannelHandlerContext 的主要功能是管理它所关联的ChannelHandler 和在同一个ChannelPipeline 中的其他ChannelHandler 之间的交互。
ChannelHandlerContext 有很多的方法,其中一些方法也存在于Channel 和Channel-Pipeline 本身上,但是有一点重要的不同。如果调用Channel 或者ChannelPipeline 上的这些方法,它们将沿着整个ChannelPipeline 进行传播。而调用位于ChannelHandlerContext上的相同方法,则将从当前所关联的ChannelHandler 开始,并且只会传播给位于该ChannelPipeline 中的下一个能够处理该事件的ChannelHandler。
下面列出了ChannelHandlerContext的方法:
方法 | 描述 |
---|---|
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 |
重写ChannelInboundHandler中的如下方法
/**
* 在读取操作期间,有异常抛出时候会调用
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
ChannelHandler.exceptionCaught()的默认实现是简单地将当前异常转发给ChannelPipeline 中的下一个ChannelHandler;如果异常到达了ChannelPipeline 的尾端,它将会被记录为未被处理;要想定义自定义的处理逻辑,你需要重写exceptionCaught()方法。然后你需要决定是否需要将该异常传播出去。
用于处理出站操作中的正常完成以及异常的选项,都基于以下的通知机制。
1.每个出站操作都将返回一个ChannelFuture。注册到ChannelFuture 的Channel-FutureListener 将在操作完成时被通知该操作是成功了还是出错了。
2.几乎所有的ChannelOutboundHandler 上的方法都会传入一个ChannelPromise的实例。作为ChannelFuture 的子类,ChannelPromise 也可以被分配用于异步通知的监听器。但是,ChannelPromise 还具有提供立即通知的可写方法:
添加ChannelFutureListener到ChannelFuture
final Channel channel = ctx.channel();
ChannelFuture write = channel.write("123");
write.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
future.cause().printStackTrace();
future.channel().close();
}
}
});
2 添加ChannelFutureListener到ChannelPromise
package com.bobo.netty.chapter06;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
/**
* 服务端出站处理
* @author [email protected]
* @create 2018-12-23 11:56
**/
public class ServerOutHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
promise.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
future.cause().printStackTrace();
future.channel().close();
}
}
});
}
}