有时候会有一系列的处理in的handler,使用fireChannelRead处理传递

转载自https://blog.csdn.net/u011702633/article/details/82051329

Netty源码解析(八) —— channel的read操作

2018年08月25日 14:41:47 靛蓝忆 阅读数:1431
 

客户端channel在建立连接之后会关注read事件,那么read事件在哪触发的呢? 
NioEventLoop中

            /**
* 读事件和 accept事件都会经过这里,但是拿到的unsafe对象不同 所以后续执行的read操作也不一样
* NioServerChannel进行accept操作
* NioChannel进行read操作
*/
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read

        @Override
public final void read() {
final ChannelConfig config = config();
//若 inputClosedSeenErrorOnRead = true ,移除对 SelectionKey.OP_READ 事件的感兴趣。
if (shouldBreakReadReady(config)) {
//清楚读事件
clearReadPending();
return;
} final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
//获取并重置allocHandle对象
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config); ByteBuf byteBuf = null;
boolean close = false;//是否关闭
try {
do {
//申请bytebuf
byteBuf = allocHandle.allocate(allocator);
//读取数据,设置最后读取字节数
allocHandle.lastBytesRead(doReadBytes(byteBuf));
//没读到数据
if (allocHandle.lastBytesRead() <= 0) {
// 梅毒到数据 释放buf
byteBuf.release();
byteBuf = null;
//如果最后读取的字节为小于 0 ,说明对端已经关闭
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
} //读到了数据
allocHandle.incMessagesRead(1);
readPending = false;
//通知pipline read事件
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());//循环判断是否继续读取 //读取完成
allocHandle.readComplete();
//通知pipline读取完成
pipeline.fireChannelReadComplete(); if (close) {//关闭连接
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
.....
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
  1. 若 inputClosedSeenErrorOnRead = true ,移除对 SelectionKey.OP_READ 事件的感兴趣。
  2. 获取并重置allocHandle对象(代理alloctor的一些功能)
  3. 读取数据,设置最后读取字节数
  4. 通知pipline read事件
  5. 通知pipline读取完成 
    这次先跟着主线走,然后再回头看细节,直接定位到事件通知 
    io.netty.channel.DefaultChannelPipeline#fireChannelRead
    /**
* 有数据读入的时候会调用(InBound) 也可以手动调用
* @param msg 客户端连接的channel
* @return
*/
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
//pipline节点类的静态方法 穿进去的head
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最后调用到io.netty.channel.DefaultChannelPipeline.HeadContext#channelRead

        /**
* 被调用read 可以向下传递
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

ctx.fireChannelRead(msg);向下个节点传递,如果自定义了handler来处理就可以拦截channelRead的bytebuf数据来进行处理,负责一直向下传递到TailContext节点处理 
io.netty.channel.DefaultChannelPipeline.TailContext#channelRead

        @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //如果不去自定义handler处理byteBuf 最终会到TailContext 来处理
onUnhandledInboundMessage(msg);
}
protected void onUnhandledInboundMessage(Object msg) {
try {
//日志提醒没有处理
logger.debug(
"Discarded inbound message {} that reached at the tail of the pipeline. " +
"Please check your pipeline configuration.", msg);
} finally {
//释放byteBuf内存
ReferenceCountUtil.release(msg);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

数据到达TailContext节点之后,再onUnhandledInboundMessage方法中打印数据未处理日志,然后释放bytebuf内存 
io.netty.channel.nio.AbstractNioByteChannel#doReadBytes读取操作的方法

    @Override
/**
* 读取数据
*/
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
//获取handle对象
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//设置读索引=写索引
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
//读取无数据到buf
return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  1. allocHandle.lastBytesRead()小于0证明没有读取到数据,要释放bytebuf
  2. 读取到数据,allocHandle.incMessagesRead(1);
  3. allocHandle.continueReading() 循环判断是否继续读取

        @Override
public boolean continueReading() {
return continueReading(defaultMaybeMoreSupplier);
} @Override
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
// Keep reading if we are allowed to read more bytes, and our last read filled up the buffer we provided.
//最后读取的字节数大于0 并且等于最大可写入的字节数
return bytesToRead > 0 && maybeMoreDataSupplier.get();
}
private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
@Override
public boolean get() {
//最后读取的字节数 是否等于最大可写入字节数
return attemptedBytesRead == lastBytesRead;
}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

最后读取的字节数大于0,并且最后读取的数据==尝试可写入的大小,即证明可以继续读取 
出现异常时候调用io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#handleReadException

        private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
RecvByteBufAllocator.Handle allocHandle) {
if (byteBuf != null) {
if (byteBuf.isReadable()) {
readPending = false;
//把已经读取到的数据 通知到pipline中
pipeline.fireChannelRead(byteBuf);
} else {
//释放bytebuf
byteBuf.release();
}
}
//读取完成
allocHandle.readComplete();
//通知pipline读取完成
pipeline.fireChannelReadComplete();
//通知pipline异常
pipeline.fireExceptionCaught(cause);
if (close || cause instanceof IOException) {
closeOnRead(pipeline);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

io.netty.channel.DefaultChannelPipeline#fireExceptionCaught

    @Override
public final ChannelPipeline fireExceptionCaught(Throwable cause) {
AbstractChannelHandlerContext.invokeExceptionCaught(head, cause);
return this;
}

最终调用到TailContext的io.netty.channel.DefaultChannelPipeline.TailContext#exceptionCaught

        @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
onUnhandledInboundException(cause);
}
protected void onUnhandledInboundException(Throwable cause) {
try {
logger.warn(
"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.",
cause);
} finally {
ReferenceCountUtil.release(cause);
}
}

和读取操作一样,最终也是要再发生异常的时候释放buf的内存

netty之handler read的更多相关文章

  1. 7.Netty中 handler 的执行顺序

    1.Netty中handler的执行顺序 Handler在Netty中,无疑占据着非常重要的地位.Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码.拦截 ...

  2. netty学习--handler传递

    在netty中的处理链pipeline中,事件是按顺序传递的,把自己拟人为netty程序,针对进来(inbound)的请求,会从head开始,依次往tail传递. pipeline采用了链表结构,he ...

  3. netty(七) Handler的执行顺序

    Handler在netty中,无疑占据着非常重要的地位.Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码.拦截指定的报文.统一对日志错误进行处理.统一对 ...

  4. netty之handler write

    转载自:https://blog.csdn.net/FishSeeker/article/details/78447684 实验过,例如channle的handler里有很多个outhandler,在 ...

  5. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  6. Netty之有效规避内存泄漏

    有过痛苦的经历,特别能写出深刻的文章 —— 凯尔文. 肖 直接内存是IO框架的绝配,但直接内存的分配销毁不易,所以使用内存池能大幅提高性能,也告别了频繁的GC.但,要重新培养被Java的自动垃圾回收惯 ...

  7. 【Netty学习】 ChannelInitializer 学习

    ChannelInitializer在Netty中是一个很重要的东西.也是4.x版本中用户接触比较多的一个类 它本身是继承ChannelInboundHandlerAdapter的.实现Channel ...

  8. 【Netty学习】Netty 4.0.x版本和Flex 4.6配合

    笔者的男装网店:http://shop101289731.taobao.com .冬装,在寒冷的冬季温暖你.新品上市,环境选购 =================================不华丽 ...

  9. 【转】Netty那点事(三)Channel中的Pipeline

    [原文]https://github.com/code4craft/netty-learning/blob/master/posts/ch3-pipeline.md Channel是理解和使用Nett ...

随机推荐

  1. GAN量化评估方法——IS(Inception Score)和FID(Frechet Inception Distance score)

    生成模型产生的是高维的复杂结构数据,它们不同于判别模型,很难用简单的指标来评估模型的好坏.下面介绍两种当前比较流行的评估生成模型的指标(仅判别图像):IS(Inception Score)和FID(F ...

  2. jvm之方法内联优化

    前言 在日常中工作中,我们时不时会代码进行一些优化,比如用新的算法,简化计算逻辑,减少计算量等.对于java程序来说,除了开发者本身对代码优化之外,还有一个"人"也在背后默默的优化 ...

  3. 第3章 Hive数据类型

    第3章 Hive数据类型 3.1 基本数据类型 对于Hive的String类型相当于数据库的varchar类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储2GB ...

  4. SparkStreaming-DStream(Discretized Stream)

    DStream(Discretized Stream)离散流 ◆ 和Spark基于RDD的概念很相似,Spark Streaming使用离散流 (discretized stream)作为抽象表示,叫 ...

  5. 清空ARP缓存

    arp -n|awk '/^[1-9]/{print "arp -d " $1}'|sh -x

  6. Next Cloud通过修改数据库表,达到替换文件而不改变分享的链接地址的效果,以及自定义分享链接地址

    Next Cloud如何通过修改数据库表,达到替换文件而不改变分享的链接地址的效果,以及自定义分享的链接地址 本文首发于我的个人博客:https://chens.life/nextcloud-chan ...

  7. Java数据结构——双端队列

    双端队列(Deque)双端队列是指允许两端都可以进行入队和出队操作的队列,其元素的逻辑结构仍是线性结构.将队列的两端分别称为前端和后端,两端都可以入队和出队.Deque继承自Queue接口,Deque ...

  8. JacaScript实现call apply bind函数

    一.call函数 模拟实现第一步:整体思路 Function.prototype.call2=function(context){ context.fn=this; //1.将函数(谁调用 即this ...

  9. python爬虫之多线程、多进程+代码示例

    python爬虫之多线程.多进程 使用多进程.多线程编写爬虫的代码能有效的提高爬虫爬取目标网站的效率. 一.什么是进程和线程 引用廖雪峰的官方网站关于进程和线程的讲解: 进程:对于操作系统来说,一个任 ...

  10. soso官方:基于相关排序的判断

    http://www.wocaoseo.com/thread-186-1-1.html 议程 概述 检索词 用户的信息需求 网页的自有信息 网页的附属信息 相关性的计算框架 概述 相关性的表象 检索词 ...