上一篇我们通过一个简单的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的更多相关文章

  1. Netty:Channel 建立后消息发送失败

    1. 问题现象 Channel 建立后消息发送失败: ChannelFuture future = DeviceManager.getBootstrap().connect(); deviceChan ...

  2. Netty 源码解析(二):Netty 的 Channel

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...

  3. Netty入门(二):Channel

    前言 Netty系列索引: 1.Netty入门(一):ByteBuf 2.Netty入门(二):Channel 在Netty框架中,Channel是其中之一的核心概念,是Netty网络通信的主体,由它 ...

  4. Netty:Netty的介绍以及它的核心组件(一)—— Channel

    1. Netty 介绍 Netty 是一个无阻塞的输入/输出(NIO)框架,它使开发低级网络服务器和客户端变得相对简单.Netty为需要在套接字级别上工作的开发人员提供了令人难以置信的强大功能,例如, ...

  5. Netty之Channel*

    Netty之Channel* 本文内容主要参考**<<Netty In Action>> ** 和Netty的文档和源码,偏笔记向. 先简略了解一下ChannelPipelin ...

  6. Go基础系列:channel入门

    Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 channel基础 chann ...

  7. Go:channel

    一.channel 在 Go 语言里,不仅可以使用原子函数和互斥锁来保证对共享资源的安全访问以及消除竞争状态,还可以使用 channel,通过发送和接收需要共享的资源,在 goroutine 之间做同 ...

  8. Netty:一种非易失堵塞client/server相框

    Netty:一种非易失堵塞client/server相框 作者:chszs.转载需注明.博客主页:http://blog.csdn.net/chszs Netty是一个异步事件驱动的网络应用框架,为J ...

  9. Java NIO学习系列二:Channel

    上文总结了Java NIO中的Buffer相关知识点,本文中我们来总结一下它的好兄弟:Channel.上文有说到,Java NIO中的Buffer一般和Channel配对使用,NIO中的所有IO都起始 ...

随机推荐

  1. 【tomcat系列】配置tomcat远程访问

    当程序部署在tomcat上后,需要监测tomcat的性能和监测tomcat的各项指标,如内存使用情况,cpu使用情况,jvm实际情况等,对于这些指标的监控,tomcat提供了访问入口,然而tomcat ...

  2. flask 密码加密 视频资料

    https://www.bilibili.com/video/av53870541/?p=33 通过在数据模型中定义函数来实现加密 from werkzeug.security import gene ...

  3. 鸟哥的Linux私房菜基础学习篇(第三版)——阅读笔记(二)

    第一章 Linux是什么 1.Linux是什么 一套操作系统 早期的Linux是针对386开发的 具有可移植性 2.Unix及Linux的发展史 1973年,Unix诞生,Ritchie等人以C语言写 ...

  4. 通过实现简单聊天室了解websocket的基础使用

    websocket基础使用 用到的依赖包 websocket的依赖 <dependency> <groupId>javax.websocket</groupId> ...

  5. 记一次华为eNSP设备网络项目基本配置过程

    下图为综合项目示例图,详细命令见图下: 屏蔽垃圾信息undo terminal monitorundo terminal trappingundo terminal loggingundo termi ...

  6. 记一次修改mysql密码

    因为马大哈的原因,没有记录自己服务器mysql的密码,试来试去试不出来只好选择重置密码. 直接上命令: 首先 vim /etc/my.cnf 在my.cnf文件中,在[mysqld]的段中加上 ski ...

  7. httpServletRequest.getCharacterEncoding()取出来是个null,怎么办?

    因为浏览器没有把正确的编码方式给服务器端. 目前,许多浏览器在Content-Type头中不会指定字符编码方式,那么容器就会使用"ISO-8859-1"方式解析POST数据,而此时 ...

  8. Hibernate一对多

    例如:一个用户可以对应多个订单  但一个订单只能对应一个用户 1.创建实体类 2.编写映射文件 <?xml version="1.0"?> <!DOCTYPE h ...

  9. volatile 手摸手带你解析

    前言 volatile 是 Java 里的一个重要的指令,它是由 Java 虚拟机里提供的一个轻量级的同步机制.一个共享变量声明为 volatile 后,特别是在多线程操作时,正确使用 volatil ...

  10. 从零开始学习R语言(二)——数据结构之“因素(Factor)”

    本文首发于知乎专栏:https://zhuanlan.zhihu.com/p/60101041 也同步更新于我的个人博客:https://www.cnblogs.com/nickwu/p/125370 ...