【Netty】 ChannelHandler的生命周期

前言

在使用Netty进行网络编程的时候,通常需要在网络连接的不同阶段进行相应的操作,比如在连接建立时,客户端向服务端发起认证,在接收到数据时对数据内容进行解析等等。那么,连接的不同阶段在netty中如何表示呢?

首先我们先分析下网络连接的生命周期:连接建立 —> 数据交互 —> 连接断开

在数据交互阶段,包括从连接中读取数据和向连接中写入数据。知道了连接的生命周期,就可以按图索骥的在各个阶段进行想要的操作。

而在 Netty 中,网络连接的不同生命周期都可以通过回调的方式来绑定相应的逻辑,这个回调接口就是ChannelHandler,这里以 ChannelInboundHandler 为例分析。

主要接口

ChanneHandler 的类依赖关系图:
在这里插入图片描述
通过上面的类结构图,我们总结一下规律:

  • ChannelHandler 有两个子接口,分别是 ChannelInboundHandlerChannelOutboundHandler,其实从字面意思就能知道,它们分别是入站和出站的接口类。
  • 如果我们自定义的业务 Handler 直接实现 ChannelInboundHandler 或者 ChannelOutboundHandler,那么我们需要实现的接口非常的多,增加了开发的难度。Netty 已经帮我们封装好了两个实现类,分别是 ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter,这样可以大大简化了开发工作。

SimpleChannelInboundHandler 也可以,额外添加了几种方法

核心生命周期方法

在ChannelInboundHandler中定义了如下和生命周期相关的接口:
在这里插入图片描述
加上在父类ChannelHandler中定义的两个:
在这里插入图片描述
这些回调接口的调用顺序是什么呢? 我们通过写一个LifeCycleHandler来看下ChannelInboundHandler的生命周期的顺序:

public class LifeCycleInBoundHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRegistered(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("channelRegistered: channel注册到NioEventLoop");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("channelUnregistered: channel取消和NioEventLoop的绑定");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("channelActive: channel准备就绪");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("channelInactive: channel被关闭");
        super.channelInactive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("channelRead: channel中有可读的数据" );
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("channelReadComplete: channel读数据完成");
        super.channelReadComplete(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("handlerAdded: handler被添加到channel的pipeline");
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx)
            throws Exception {
        System.out.println("handlerRemoved: handler从channel的pipeline中移除");
        super.handlerRemoved(ctx);
    }
}

客户端发送一次请求:

handlerAdded: handler被添加到channel的pipeline
channelRegistered: channel注册到NioEventLoop
channelActive: channel准备就绪
channelRead: channel中有可读的数据
channelReadComplete: channel读数据完成
channelReadComplete: channel读数据完成

客户端发送多次请求,仅列出后续的日志:

channelReadComplete: channel读数据完成
channelReadComplete: channel读数据完成

channelReadComplete: channel读数据完成
channelReadComplete: channel读数据完成

channelReadComplete: channel读数据完成
channelReadComplete: channel读数据完成

通过执行结果我们发现,第一次的时候执行 handlerAdded()、channelRegistered()、channelActive(),后面就不会被执行了。

客户端的每次请求时,都会触发 channelRead() 和 channelReadComplete() 两个核心方法。

关闭客户端:

二月 03, 2021 9:16:59 下午 io.netty.channel.DefaultChannelPipeline onUnhandledInboundException
警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.io.IOException: 远程主机强迫关闭了一个现有的连接。
	at sun.nio.ch.SocketDispatcher.read0(Native Method)
	at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
	at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
	at sun.nio.ch.IOUtil.read(IOUtil.java:192)
	at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
	at io.netty.buffer.PooledByteBuf.setBytes(PooledByteBuf.java:253)
	at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1134)
	at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:350)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:151)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

channelInactive: channel被关闭
channelUnregistered: channel取消和NioEventLoop的绑定
handlerRemoved: handler从channel的pipeline中移除

总结,人为的关闭通道或者其他因素(比如:网络故障等),则会触发 channelInactive、channelUnregistered、handlerRemoved 的执行。

生命周期总结

从上面结果可以知道,从连接建立到连接断开,handler的生命周期回调接口调用顺序如下:
在这里插入图片描述

  • handlerAdded :当检测到新连接之后,调用 ch.pipeline().addLast(new LifeCycleHandler()); 之后的回调,表示在当前的 channel 中,已经成功添加了一个 handler 到双向链表(pipeline) 中。

  • channelRegistered:这个回调方法,表示当前的 channel 的所有的逻辑处理已经和某个 NIO 线程建立了绑定关系,从线程池里面去抓一个线程绑定在这个 channel 上,这里的 NIO 线程通常指的是 NioEventLoop。

    对应 worker线程池的NioEventLoop

  • channelActive:当 channel 的所有的业务逻辑链准备完毕,channel 的 pipeline 中已经添加完所有的 handler 以及绑定好一个 NIO 线程之后,这条连接算是真正激活了,接下来就会回调到此方法。

    说明该channel准备就绪,可以使用了

  • channelRead:客户端向服务端发来数据,每次都会回调此方法,表示有数据可读。

  • channelReadComplete:服务端每次读完一次完整的数据之后,回调该方法,表示数据读取完毕。

  • channelInactive: 表面这条连接已经被关闭了,这条连接在 TCP 层面已经不再是 ESTABLISH 状态了。

  • channelUnregistered: 既然连接已经被关闭,那么与这条连接绑定的线程就不需要对这条连接负责了,这个回调就表明与这条连接对应的 NIO 线程移除掉对这条连接的处理。.

    与channelRegistered对应,当连接关闭后,释放绑定的workder线程;

  • handlerRemoved:给这条连接上添加的所有的业务逻辑处理器都给移除掉。

ChannelHandler 回调方法的执行顺序为:

  • 连接请求: handlerAdded () -> channelRegistered () -> channelActive () -> channelRead () -> channelReadComplete ();
  • 数据请求: channelRead () -> channelReadComplete ();
  • 通道被关闭:channelInactive () -> channelUnregistered () -> handlerRemoved ()。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Exception caught when during method invocation. request:net.risesoft.rpc.itemAdmin.DocumentManager.edit4Position(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) requestId=1771270236171928205 java.lang.reflect.InvocationTargetException: null at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.weibo.api.motan.rpc.DefaultProvider.invoke(DefaultProvider.java:64) at com.weibo.api.motan.rpc.AbstractProvider.call(AbstractProvider.java:52) at com.weibo.api.motan.transport.ProviderMessageRouter.call(ProviderMessageRouter.java:98) at com.weibo.api.motan.transport.ProviderProtectedMessageRouter.call(ProviderProtectedMessageRouter.java:75) at com.weibo.api.motan.transport.ProviderMessageRouter.handle(ProviderMessageRouter.java:93) at com.weibo.api.motan.transport.support.DefaultRpcHeartbeatFactory$HeartMessageHandleWrapper.handle(DefaultRpcHeartbeatFactory.java:98) at com.weibo.api.motan.transport.netty4.NettyChannelHandler.processRequest(NettyChannelHandler.java:155) at com.weibo.api.motan.transport.netty4.NettyChannelHandler.processMessage(NettyChannelHandler.java:133) at com.weibo.api.motan.transport.netty4.NettyChannelHandler.access$000(NettyChannelHandler.java:32) at com.weibo.api.motan.transport.netty4.NettyChannelHandler$1.run(NettyChannelHandler.java:73) at java.util.concurrent.ThreadPoolExecutor.runWorker(Threa是哪里的问题
最新发布
07-14

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值