3、我眼中的Netty—ChannelHandler以及ChannelPipeline

这篇文章讲的是的ChannelHandler、Context、Pipeline、SockeChannel相关的核心组件关系以及以及使用技巧上的验证,有关的代码以及在实操的这一篇文章讲过了,这里聚焦于此部分技术概念的验证。

1、ChannelHandler体系结构描述

Channel相当于是搭起来客户端与服务端之间通信的一个管道,不过在客户端与服务端之间是存在编码解码以及业务相关的处理的,而这些处理我们程序猿是可以干预的,可以干预的这些过程都都是发生在与Channel板顶的Pipeline中。Pipeline可以看做是一个责任链表的实现,每一步具体的操作都是通过Handler实现的,根据数据流动的方向不同又可以分为出站以及站。不同的Handler之间是需要进行相互响应的,每一个Handler都会被一个HandlerContext包裹,Handler之间的交互可以看做是通过HandlerContext操作的。HandlerContext中包含了Handler多有相关的信息也可以见他看做是Handler的一个上下文环境。

2、ChannelHandler中有哪些声明周期

关于ChannelHandler中的声明周期,可以理解为是对于他相关的组件相关事件的一系列相应。对声明周期的介绍也按照一定的层次从大到小,让后再在水平的方向上来进行一个介绍,这样可以帮助我们理解Channel生命周期的API函数有哪些以及他们的作用是什么。

  • Channel的声明周期
状态描述
channelUnregisteredChannel已被创建但是还没有注册到EventLoop
channelRegistreredChannel已经被注册到EventLoop
channelInactiveChannel没有连接到远程节点
channelActiveChannel处于活动状态(已经连接到它的远程节点)他现在可以接受和发送数据了
  • ChannelHandler的声明周期

状态描述
handlerAdded当吧ChannelHandler添加到ChannelPipeline中时被调用
handlerRemoved当从ChannelPipeline中移除改ChannelHandler时被调用
exceptionCaught当处理过程如果ChannelPipeline中有错误产生时候被调用
  • ChannelInboundHandler生命周期
状态描述
channelReadComplete当Channel上的一个读操作完成时候被调用
channelRead当从Channel读取数据时候被调用
channelWritabilityChanged
userEventTriggered
  • ChannelOutboundHandler声明周期
状态描述
bind当讲请求绑定到本地时候被调用
connect当请求连接到远程节点时候被调用
disconnect当请求将Channel从远程节点断开时被调用
close当请求关闭Channel时被调用
deregister当请求将Channel从他的EventLoop注销时调用
read当请求从Channel读取跟多数据时候被调用
flush当请求通过Channel将对立数据刷到远程节点时候调用
write当请求通过Channel将数据写到远程节点时候被调用

3、通过Context进行链调用的流程以及发生在此流程中的数据传递

通过Context进行调用的过程可以参考小结-6的划分成为两种类型的一种是从头开始还有一种就是通过中间任意一个context开始的,当调用fire方法的时候本质上Context是去寻找下一个符合条件——同一流向的那个Context,并进行操作。Context是对Handler的包裹,context组成的双向量表就是一个数据处理的通道。数据就是顺着这个链表朝下处理的,调用fire类型的操作是传递了我们当前方法的处理后的结果,通过这样的一个形式就形成了数据的一个传递。至于Handler调用先后顺序的演示即可以参考在编解码器那篇文章也可以看Netty实操章节中关于非阻塞模式实现的那部分Demo。

4、ChannelPromise与ChannelFuture区分联系

ChannelPromise是ChannelFuture的一个 子类,其定义了一些可写的方法,如setSuccess()和setFailure(),从而使ChannelFuture不 可变

这一部分的是指还待补充!!!

5、消息释放的场景

消息释放场景背后对应的技术是我们在ByteBuffer与ByteBuf对比文章中说道的引用计数,个人对引用计数的接触最早是在JVM虚拟机中关于垃圾回收算法的。目前的理解是如果对象在堆中对象是可以被正常回收之类的但是我们的Buf的使用有一个用法就是直接内存分配。直接内存是堆外内存个人理解是他的回收不收JVM的控制所以需要我们手工的干预。总体的一个干预原则就是当我们不需要一个ByteBuf的时候我们需要通过手工来将它释放掉,为了简化这一操作Netty中也提供了一种具体的实现——SimpleChannelInboundHandler(这个实现会在消 息被 channelRead0()方法消费之后自动释放消息),当然有要释放的场景那么自然就有要保留引用计数的场景。

当某个 ChannelInboundHandler 的实现重写 channelRead()方法时,它将负责显式地 释放与池化的 ByteBuf 实例相关的内存

public class DiscardHandler extends ChannelInboundHandlerAdapter { 
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) { 
		ReferenceCountUtil.release(msg);
 	} 
} 

个人猜测:channelRead方法会被调用多次,如果当前的msg经过自己处理后不再需要往后传那么就应该尽快的将它释放掉,避免对内存的占用。

为了帮助你诊断潜在的(资源泄漏)问题,Netty提供了class ResourceLeakDetector① 级 别 , 它将对你应用程序的缓冲区分配做大约 1%的采样来检测内存泄露。相关的开销是非常小的

泄露检测级别可以通过将下面的 Java 系统属性设置为表中的一个值来定义: java -Dio.netty.leakDetectionLevel=ADVANCED
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZZsoYCgp-1651676632972)(F:\typroa\aimages\image-20220504224539217.png)]
消息的释放场景既然是没有的消息那么已经出站的数据机遇也是应该被释放的:

public class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter { 
	@Override
	public void write(ChannelHandlerContext ctx,
		Object msg, ChannelPromise promise) { 
		ReferenceCountUtil.release(msg); 
        //通知ChannelPromise数据已经被处理
		promise.setSuccess();
 	} 
}

6、通过Pipeline调用的走的是全链路通过Context调用的走的是半链路

lPromise数据已经被处理
promise.setSuccess();
}
}


### 6、通过Pipeline调用的走的是全链路通过Context调用的走的是半链路

这一部分内容已经在源码剖析的那一部分说过了,主要就是通过AbstractChannelHandlerContext.invokeChannelRead(head, msg);直接传递进去的就是头部节点也就是双向链表的头部节点;如果要是通过其中的一个context调用的话他则是通过一个do-while的循环体来寻找到下一个符合条件的节点,从而来进行调用。
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、付费专栏及课程。

余额充值