四Netty组件类--4ChannelHandler
四Netty组件类--4ChannelHandler
17.3 ChannelHandler功能说明
17.3.1 ChannelHandler接口功能
1 channelHandler:
channelHandler实际为inbound和outbound的拦截器(拦截过滤器原理)
handlerAdded(ChannelHandlerContext ctx):
对于server端,当客户端向server端连接时,先前在server端加入的ServerBootstrapAcceptor(inboundHandler对象),其实现channelRead方法,会为每个新创建的server端childChannel.pipeline.addLast(childHandler),向每个childchannel中添加ServerBootstrap.childHandler()中配置的childHandler类,在addLast操作加入pipeline时,会触发handler.handlerAdded的操作,将handler绑定到channel,然后handler可以处理event事件。
public interface ChannelHandler {
////////////////////////////////
// Handler生命周期方法//
////////////////////////////////
/**
当channelpipeline.addLast(handler)时,需要最终执行handler.handlerAdded操作,此后handler才可以处理event事件
*/
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
/////////////////
// Annotations注解 //
/////////////////
/**被@Sharable注解的ChannelHandler,可以被多次的添加入不同的pipeline种,且只需要new一次,即一个共享对象*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
// no value
}
/** 注意一:
被@Skip注解的channelHandler,不会被channelpipeline调用;
这个注解,只有当你的handler method的实现是为了从一个handler传递event给下一个handler时,需要注解@skip。
例如,如下操作:
* <pre>
* {@code @Skip}
* {@code @Override}
* public void channelActive({@link ChannelHandlerContext} ctx) {
* ctx.fireChannelActive(); // 什么操作不做,只是传递事件给下一个handler
* }
* </pre>
*
* {@link #handlerAdded(ChannelHandlerContext)} and {@link #handlerRemoved(ChannelHandlerContext)} 这两个方法不能不能通过ctx传递,只能通过do nothing传递事件
*
* <pre>
* {@code @Skip}
* {@code @Override}
* public void handlerAdded({@link ChannelHandlerContext} ctx) {
* // 必须do nothing
* }
* </pre>
*
* <p>注意二:
当implement并override该@skip注解的方法,这个方法就不会skip跳过。
同样的,可以override一个没有注解@skip的method,然后传递event。*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Skip {
// no value
}
}
注意Netty5.0变化:
精简handler类型继承关系
ChannelInboundHandler和ChannelOutboundHandler被合并成ChannelHandler。现在ChannelHandler中既有inbound handler函数也有outbound handler函数。
ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter和ChannelDuplexHandlerAdapter被弃用然后替换为
ChannelHandlerAdapter
。
因为现在已经无法区分一个handler是inbound handler还是outbound handler,所以CombinedChannelDuplexHandler已经被替换为
ChannelHandlerAppender
。
目前,netty5.0不再更新维护。
Netty曾经有Netty 5这个版本,但是后来被废弃掉了,现在使用的都是Netty 4的版本。之所以被废弃,最主要的原因是Netty 5中使用了ForkJoinPool,增加了代码的复杂性却没有很明显的性能提升,同时增加了额外的多版本分支的维护工作。
2 ChannelInboundHandler和ChannelOutboundHandler使用区别
2.1 ChannelInboundHandler
public interface ChannelInboundHandler extends ChannelHandler {
///////////////////////////////////
// inbound事件event处理方法 //
///////////////////////////////////
/**如果Throwable被抛出,触发该方法*/
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
/**ChannelHandlerContext其内的channel注册在其内的EventLoop的selector上,触发该方法的操作:
client端:通过tcp建立连接,获取socketchannel,并注册在selector上;
server端:bind操作后,创建serversocketchannel,通过将通道注册到selector监听客户端连接时*/
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
//取消注册到selector调用。通常在channel.close时触发,先channelInactive,再channelUnregistered事件
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
/**ChannelHandlerContext其内的channel现在是active状态
active:底层socket的isOpen()和isConnected()为true,也就是socket为open且connect*/
void channelActive(ChannelHandlerContext ctx) throws Exception;
/**通常由close触发,先inactive,再unregister事件 */
void channelInactive(ChannelHandlerContext ctx) throws Exception;
/**在当前channel从peer处read到message,触发该方法*/
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
/**当channelRead读取了最后一条message,触发该方法。
如果ChannelOption#AUTO_READ是off关闭状态,不在尝试从当前channel读取inbound数据,除非由ChannelHandlerContext#read()调用*/
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
/**定制化事件触发器 */
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
/**channel的writable状态改变时,调用该方法*/
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
channel的状态之间关系:
1 channel注册到nioeventloop后,为registered状态,pipeline.fireChannelRegistered;
2 然后判断channel是否为active:(底层socket为open或者connect时,channel为active状态)
2.1 如果channel第一次注册,执行pipeline.fireChannelActive;
2.2 如果channel不是第一次注册,执行beginRead,准备读
2.2 ChannelOutboundHandler
public interface ChannelOutboundHandler extends ChannelHandler {
////////////////////////////////////
// Outbound 事件处理方法 //
////////////////////////////////////
/**serverbootstrap.bind()绑定操作开始时
* @param promise the {@link ChannelPromise} to notify once the operation completes */
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
//client启动,connect连接操作
void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception;
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**一旦当前注册的eventloop执行deregister操作,就调用*/
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**是ChannelHandlerContext#read()执行时的拦截器
这里了是注册read事件,不是触发网络读写事件*/
void read(ChannelHandlerContext ctx) throws Exception;
/**通过pipeline写消息到通道对应的写缓冲区。当flush()时,才把数据写入到真正的channel中
调用channel.write时触发*/
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
/**将先前写入的所有messages,flush out(刷新入channel中)————调用channel.flush时触发
* @param ctx the {@link ChannelHandlerContext} for which the flush operation is made*/
void flush(ChannelHandlerContext ctx) throws Exception;
netty4.0使用如上的源码。netty5.0虽然没有这两个类,但是inbound和outbound的概念依然存在。
Inbound和Outbound操作:
inbound入端操作:基本由selector事件就绪,进行触发,方法以channel开头。这些操作是外部信息,in进入系统,进行的信息handler处理(主要是channel状态的变化,如active、connected、complete等);
outbound出端操作:是内部系统信息传向外部out,进行的触发操作(除了read,其他都是通过调api,例如bind、connect、close、write)--(out主要是业务程序,调用的方法操作)。
注意:in、out的流向处理
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new MyChannelInboundHandler1());
ch.pipeline().addLast(new MyChannelInboundHandler2());
ch.pipeline().addLast(new MyChannelOutboundHandler1());
}
通过pipeline添加的ChannleHandler,构成handler处理链(in1>in2>out1)。
inbound消息,会流经所有添加的ChannelInboundHandler,出站的消息不一定流经所有的ChannelOutboundHandler。
通过某个ChannelInboundHandler传入参数ChannelHandlerContext的writeAndFlush()方法写出的数据,不会流经后续链路的outHandler;
但是通过ctx.channel().writeAndFlush()写出的数据,则会流经所有后续的outHandler。
例如,ChannelInboundHandler(ChannelHandlerContext ctx,Object msg),
//该种方式,不会传递到后续的outboundHandler
ctx.writeAndFlush("Welcome to Netty......");
想要执行后续的out——也就是改变信息流向。(这样才能通过out的处理,把信息发送给client或者server本系统之外,让对方可以收到处理过的response信息)
有两个方法:
(1)
ctx.channel().writeAndFlush("Welcome to Netty......");
(2)把outbound插入handler处理链的前段(就是放在ctx.writeAndFlush所在的inboundHandler的前端)
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new MyChannelInboundHandler1());
ch.pipeline().addLast(new MyChannelInboundHandler2());
ch.pipeline().addFirst(new MyChannelOutboundHandler1());
}
具体详细的代码示例,看原文章
17.3.2 ChannelHandlerAdapter
channelHandlerAdapter为接口适配器模式。该类是抽象类abstract class,两个子类实现该抽象类,然后创建channelHandler类,可以接口多态模式,创建对象,但是对应方法,可以是子类中的方法。例如,这里创建server端的channelhandler类,但是其既需要inbound方法,又需要outbound方法,因此,使用该adapter类,可以创建一个适配器抽象类。继承该类,就可以实现一个既有inbound又有outbound方法的handler。
17.3.3ChannelInitializer
该类作用:
可知ChannelInitializer是入端的channelHandler,会在inbound,的过程中调用并初始化channelPipeline上的处理器。
在某channel注册到eventloop后,对channel执行initChannel操作。ChannelInitializer会先注册添加入channel.pipeline()中,执行到该CI,会将类内init方法内的handler添加入pipeline中。在初始化完成后,ChannelInitializer会将自己从pipeline中移除。