四Netty组件类--4ChannelHandler

四Netty组件类--4ChannelHandler

17.3 ChannelHandler功能说明

image-20220920133026772

image-20221128161956291

image-20230310155736328

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事件。

image-20230310155759173
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

image-20221129161926245

channelHandlerAdapter为接口适配器模式。该类是抽象类abstract class,两个子类实现该抽象类,然后创建channelHandler类,可以接口多态模式,创建对象,但是对应方法,可以是子类中的方法。例如,这里创建server端的channelhandler类,但是其既需要inbound方法,又需要outbound方法,因此,使用该adapter类,可以创建一个适配器抽象类。继承该类,就可以实现一个既有inbound又有outbound方法的handler。

17.3.3ChannelInitializer

image-20221128162244193

该类作用:

可知ChannelInitializer是入端的channelHandler,会在inbound,的过程中调用并初始化channelPipeline上的处理器。

在某channel注册到eventloop后,对channel执行initChannel操作。ChannelInitializer会先注册添加入channel.pipeline()中,执行到该CI,会将类内init方法内的handler添加入pipeline中。在初始化完成后,ChannelInitializer会将自己从pipeline中移除。

posted @ 2023-03-10 17:23  LeasonXue  阅读(161)  评论(0编辑  收藏  举报