Netty:Channel
上一篇我们通过一个简单的Netty代码了解到了Netty中的核心组件,这一篇我们将围绕核心组件中的Channel来展开学习。
Channel的简介
Channel代表着与网络套接字或者能够进行IO操作(read、write、connect或者bind)的组件的联系,一个Channel向用户提供了如下内容:
1、Channel当前的状态,比如是否打开、是否连接;
2、Channel的配置参数,比如接收缓冲区的大小;
3、Channel支持的IO操作(read、write、connect或者bind);
4、用于支持处理与Channel关联的所有IO事件和请求的ChannelPipeline组件。
Netty中的所有IO操作都是异步的。这意味着任何IO调用都将立即返回,而不能保证所请求的IO操作在调用结束时完成。相反,将返回一个带有ChannelFuture的实例,该实例将在请求的IO操作成功、失败或取消时通知应用。
Channel可以具有父级,具体取决于其创建方式。例如,SocketChannel在ServerSocketChannel接受它时,通过parent()方法将ServerSocketChannel作为它的父级返回。层次结构的语义取决于Channel所属的传输实现。例如,可以编写一个新的Channel实现,以创建共享一个套接字连接的子通道,就像BEEP和SSH一样。
某些传输公开了特定于该传输的其他操作,可以将Channel向下转换为子类型以调用此类操作。例如,对于旧的IO数据报传输,DatagramChannel提供了join /leave操作。
一旦使用完Channel,调用close()或close(ChannelPromise)释放资源就显得尤为重要,这样做可以确保以适当的方式(即文件句柄)释放所有资源。
Channel的方法
学习Channel提供的方法,其实可以结合上述简介部分来看。
比如,有关Channel的状态,我们可以看到这几个方法:


是否打开看isOpen方法,是否注册到EventLoop看isRegistered方法,是否连接看isActive方法。
Channel的配置参数方法可以看config方法:

该方法返回了ChannelConfig对象,这个接口定义了Channel的配置参数集合,但是在实际应用中,需结合实际的传输协议来设置具体的ChannelConfig,比如对于TCP/IP协议,需要具体被设置的对象就是SocketChannelConfig。

通过pipeline()方法,可以获取到Channel的ChannelPipeline对象,正如上文所述,ChannelPipeline也是Netty的核心组件,它可以理解为是ChannelHandler的容器,用于处理Channel的所有事件。
简介中提到了Netty异步操作会返回ChannelFuture对象,那么在Channel所提供的方法中是如何体现和这个对象的交互的呢?答案就是我们可以从closeFuture()方法中看到ChannelFuture对象,这个方法告诉我们Channel关闭时将返回用于通知的ChannelFuture,只有Channel真正的被关闭完成后,才会通过ChannelFuture回调通知到应用。

关于ChannelPipeline、ChannelHandler和ChannelFuture,我们将在后续的文章中学习。
除了上述方法,Channel还有很多方法,比如:

每一个Channel都可以有自己的id,ChannelId有2个主要的方法,asShortText()会返回短的但是全局不唯一的标识符,asLongText()会返回长的同时全局唯一的标识符。
eventLoop()会返回Channel所注册之上的EventLoop,EventLoop也是Netty的核心组件,我们也将在后续的文章中学习。
parent()会返回Channel的父级,如果没有父级则返回null。
另外Channel中还有一个内部类Unsafe,这个类的方法不建议外部使用,仅限于Netty内部使用。
源码流程
以服务端为例,在我们服务端DEMO中,可以看到代码中并未显式的创建Channel和将Channel注册到EventLoop,那么服务端启动时是如何创建Channel及将Channel注册到EventLoop呢?让我们一起来看下启动的源代码,一窥究竟。

首先,在Server中调用的是bind方法,bind里面调用的是doBind。

在doBind中,进入了启动的核心逻辑initAndRegister。

initAndRegister,顾名思义,就是初始化和注册,初始化前就有Channel的创建,注册里面包含了Channel注册到EventLoop的过程。

让我们继续看Channel是如何创建的,调用了ReflectiveChannelFactory的newChannel方法,是通过反射的方式来创建的Channel。

进入到我们的DemoServer中的NioServerSocketChannel,继续查看它的构造方法。



经过一步一步的跟进,可以在AbstractChannel看到Channel的构造过程,在这个方法中,我们可以看到熟悉的id、parent、unsafe和pipeline,这些都是Channel中的关键属性或者对象。


创建完成后,先忽略初始化,咱们再来看下Channel是如何注册的?我们来看config().group().register(channel)。

先来看下next()方法返回的是什么?


最后可以发现next()方法返回的是SingleThreadEventLoop,其实到这一步我们也可以知道Netty中的MultithreadEventLoopGroup里面可以获取到很多SingleThreadEventLoop,而SingleThreadEventLoop是一个单线程任务执行的事件循环,在它的父类SingleThreadEventExecutor中我们可以找到这个Thread。
继续看register方法。

最后会进入到真正的执行注册的AbstractUnsafe类的register0方法中。

进一步跟进,调用doRegister方法。

最终会进入到AbstractNioChannel的doRegister方法。在这里我们可以看到NIO的身影,比如SelectionKey、Selector等。到这里,我们就可以看到Channel是如何注册到NioEventLoop,它的底层本质也就是NIO中的Channel注册到Selector,因为在NioEventLoop中集成了一个Selector。

至此,我们已经知道了Channel的创建和注册的过程。
最后总结一下:
1、服务端这块,Channel创建的过程是通过反射来创建的,最终是进入到了AbstractChannel的构造函数中;
2、服务端这块,Channel注册到EventLoop的过程,本质上也就是NIO中的Channel注册到Selector的过程;
3、看源码的过程中,其实还有很多有意思的细节,比如创建Channel的同时其实ChannelPipeline也创建好了,注册的时候其实会判断注册线程和当前线程是不是一个线程来看是立即注册还是新起线程注册?这些后面再来进一步的学习和分析。
Netty:Channel的更多相关文章
- Netty:Channel 建立后消息发送失败
1. 问题现象 Channel 建立后消息发送失败: ChannelFuture future = DeviceManager.getBootstrap().connect(); deviceChan ...
- Netty 源码解析(二):Netty 的 Channel
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...
- Netty入门(二):Channel
前言 Netty系列索引: 1.Netty入门(一):ByteBuf 2.Netty入门(二):Channel 在Netty框架中,Channel是其中之一的核心概念,是Netty网络通信的主体,由它 ...
- Netty:Netty的介绍以及它的核心组件(一)—— Channel
1. Netty 介绍 Netty 是一个无阻塞的输入/输出(NIO)框架,它使开发低级网络服务器和客户端变得相对简单.Netty为需要在套接字级别上工作的开发人员提供了令人难以置信的强大功能,例如, ...
- Netty之Channel*
Netty之Channel* 本文内容主要参考**<<Netty In Action>> ** 和Netty的文档和源码,偏笔记向. 先简略了解一下ChannelPipelin ...
- Go基础系列:channel入门
Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 channel基础 chann ...
- Go:channel
一.channel 在 Go 语言里,不仅可以使用原子函数和互斥锁来保证对共享资源的安全访问以及消除竞争状态,还可以使用 channel,通过发送和接收需要共享的资源,在 goroutine 之间做同 ...
- Netty:一种非易失堵塞client/server相框
Netty:一种非易失堵塞client/server相框 作者:chszs.转载需注明.博客主页:http://blog.csdn.net/chszs Netty是一个异步事件驱动的网络应用框架,为J ...
- Java NIO学习系列二:Channel
上文总结了Java NIO中的Buffer相关知识点,本文中我们来总结一下它的好兄弟:Channel.上文有说到,Java NIO中的Buffer一般和Channel配对使用,NIO中的所有IO都起始 ...
随机推荐
- Function.prototype.call.apply()方法
在看uncurrying化函数时候,碰到了Function.prototype.call.apply()的用法: 先说说uncurrying()函数: Function.prototype.uncur ...
- 基于JWT实现token验证
JWT的介绍 Json Web Token(JWT)是目前比较流行的跨域认证解决方案,是一种基于JSON的开发标准,由于数据是可以经过签名加密的,比较安全可靠,一般用于前端和服务器之间传递信息,也可以 ...
- 7,MapReduce基础
目录 MapReduce基础 一.关于MapReduce 二.MapReduce的优缺点 三.MapReduce的执行流程 四.编写MapReduce程序 五.MapReduce的主要执行流程 Map ...
- OpenCV3入门(十四)图像特效—挤压、哈哈镜、扭曲
一.图像挤压特效 1.原理 图像压效果本质的图像坐标的非线性变换,将图像向内挤压,挤压的过程产生压缩变形,从而形成的效果. 挤压效果的实现是通过极坐标的形式,设图像中心为O(x,y),某点距离中心O的 ...
- 在d盘创建文件夹,里面有aaa.txt/bbb.txt/ccc.txt,然后遍历出aaa文件夹下的文件(新手)
//导入的包.import java.io.File;import java.io.IOException;//创建的一个类.public class zy { //公共静态的主方法. public ...
- 2019.3.14解题报告&补题报告
A题 题意: 输入r, c,代表r*c的矩阵,接下来一行,是r个数,代表每一行里最大的数:接下来一行,是c个数,代表每一列中的最大数.求所给数据是否冲突. 思路:判断r个数中最大数maxr和c个数中最 ...
- BeanShell断言:根据响应的2个数据的比较结果来决定断言
需求:目前有一个请求,请求的响应中有2个值,aaa和bbb,我们比较aaa和bbb,如果aaa大于bbb,则断言通过,否则失败. 1.添加一个Dummy Sampler,模拟请求. 2.添加2个正则表 ...
- Fiddler5 发送HTTP请求
1.Fiddler Composer发送HTTP请求 Composer的编辑模式主要有2种:Parsed模式和Raw模式. 实例1:Composer发送get请求 实例2:Composer发送post ...
- SpringMVC框架——文件的上传与下载
使用SpringMVC框架做个小练习,需求: 1.单个图片上传并显示到页面中: 2.多个图片上传并显示到页面中: 3.上传文件后下载文件: 1.pom.xml中添加依赖 <!-- 文件上传 -- ...
- 二维数组及Arrays工具类
1.二维数组 概念: 数组中的每一个元素类型都是一维数组 二维数组初始化方式: 静态初始化: 格式: 元素类型[][] 数组名 = new 元素类型[][]{{一维数组1},{一维数组2},{一维数组 ...