Netty核心概念(5)之Channel
1.前言
上一节讲了Netty的第一个关键启动类,启动类所做的一些操作,和服务端的channel固定的handler执行过程,谈到了不管是connect还是bind方法最终都是调用了channel的相关方法,此节开始对channel进行说明。channel设置的概念非常多,而且都很重要,先放个NIO的客户端Channel的类结构图。
2.主要概念
2.1 channel
channel就是直接与操作系统层到交道的数据通道了,可能是java提供的,也可能是通过native方法自己扩展了C++功能的渠道,但是不管哪类,都有一个基础的定义。下面是channel中主要定义的接口方法:
id():获取该channel的标识
eventloop():获取该channel注册的线程池
parent():获取该channel的父channel,NIO没有父channel,一般为null
config():获取该channel的配置
isOpen():该channel是否打开状态
isRegistered():该channel是否注册到线程池中
isActive():该channel是否可用
metadata():该channel的元数据
localAddress():该channel的本地绑定地址端口
remoteAddress():该channel连接的对端的地址端口
closeFuture():该channel关闭时触发的future
isWritable():该channel当前是否可写,只有IO线程会处理可写状态
bytesBeforeUnwritable():该channel还能写多少字节
unsafe():获取该channel的unsafe操作对象,对于channel的读写,一般不直接操作channel,而是转交给unsafe对象处理,channel本身通常只做查询状态,获取相关字段内容的操作。
alloc():获取分配的缓冲区
read():进行read操作
write():进行write操作
上面的方法我们看见了一个不熟悉的unsafe对象,这个也是一个比较重要的概念,理解该类在整个结构所处的位置作用,对于理解框架有较大的帮助。Unsafe被直接定义在Channel接口内部,意味着该接口是与Channel绑定的,上述方法的时候也解释过该类作用。channel本身不直接做相应的工作,交给unsafe方法调用。下图是unsafe的接口定义:
recvBufAllocHandle():获取处理读取channel数据之后处理的handler
localAddress():本地地址端口
remoteAddress():远程地址端口
register():将channel注册到线程池中
bind():服务端绑定本地端口
connect():客户端连接远程端口
disconnect():断开连接
close():关闭channel
closeForcibly():强制关闭
deregister():移除线程池的注册
beginRead():准备读取数据
write():写入数据
flush():强制刷新
voidPromise():特殊的promise
outboundBuffer():获取输出数据的buffer操作类
看到上面的一系列接口就能够明白,实际操作channel的是unsafe类,但是是直接操作unsafe类吗?比如绑定端口的时候确实调用的是channel.bind方法啊,实际上这里还涉及其它概念,绕了一圈进行操作的。unsafe看名称也应该明白,这个对channel进行操作的类是个线程非安全的类,所以一般通过Netty本身的结构设计,保证线程隔离,才能放心使用。当然如果不自己定义一种IO方式,基本上使用现在Netty封装好的不会有什么问题,如果自己造轮子,这个就要额外注意了。
2.2 ChannelPipeline
在前面陆续都提到了这个pipeline,本小节就好好聊聊这个类的作用。先不看接口定义,先关注该类在整个结构所处的位置。打开AbstractChannel,仔细研究一下这个类抽象的channel类,你就会有所收获。channel的主要操作方法大部分都是通过pipeline来完成的,如:bind,connect,close,deregister,flush,read,write等。奇怪吗?并不是我们上面说的由unsafe处理。但是这并不矛盾,unsafe是对最底层最基础的处理,我们会有一系列的业务层需要处理,比如bind时对socket的参数设置交由handler处理,所以channel会将相关操作委托给pipeline处理,pipeline经过一系列操作,最后调用unsafe的相关动作,最终回到channel。pipeline翻译是管道,这里感觉更像流水线操作。
现在再来看pipeline的基本定义就不会觉得突兀了。
pipline的方法很多,但是分成两大类:1.注册handler;2.使用handler;截图是使用handler的方法,注册handler的方法很多,这里不进行介绍。ChannelPipeline没有那么多的实现类,基础的就是DefaultChannelPipeline,所以直接对该类的部分方法解析。
1.先是注册handler的方法究竟干了什么,拿例子中使用的addLast()方法为例:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler); newCtx = newContext(group, filterName(name, handler), handler); addLast0(newCtx); // If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
} EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
要看懂这段代码还有个内容要注意,tail和head,以及handlerContext(这个是handler相关的概念,这里不进行介绍)。tail和head是pipeline持有的handler头结点和尾结点,看见addLast的时候就应该明白,handler是以链式结构串起来的,在前面也说过,handler是职责链模式,是有先后顺序的。这个方法就是将handler放在职责链尾。中间有个过程,将handler用context包装了,这个不是本节的重点,之后handler章再介绍。
2.接下来就是pipeline的重点,其是通过什么方式操作channel的,或者说是操作channel的过程是怎样的。
public final ChannelFuture bind(SocketAddress localAddress) {
return tail.bind(localAddress);
}
基本的channel操作都是通过默认的tail完成的,这些操作有bind,connect,close,deregister,flush,read,write。tail是一个handlerContext,这里会涉及一些handlerContext的内容,简略说下吧:在之前pipeline添加handler的时候,生成了context,context构成了链结构,其知道自己的前后handler是哪个。其他的不细说,最终是通过tail的这个handler不断的早它前一个out类型的handler,最终找到head,看HeadContext类你就会明白了,该类获取了channel的unsafe对象,所有操作都由该对象完成,这样整个环节就连上了。由channel生成channelpipeline和unsafe对象,所有操作交给pipeline,pipeline从tail一直搜索到head,最后由head获取channel的unsafe方法,最终进行相关操作。
还有一类方法这里也进行介绍一下,牵扯到handlerContext职责链的运行过程,不细讲。
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
这个从head开始调用
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive(); readIfIsAutoRead();
}
然后又调用了自己的fireChannelActive方法,从head往后找,next的in类型的方法,推动了整个职责链的操作了。所以pipeline的fireChannelActive()方法是起始方法,推动职责链的调用。
3.后记
channel一共有三个重要的概念:
1.Channel本身不做事情,将事件都交给ChannelPipeline。
2.ChannelPipeline本身也不做什么,其主要是控制handler链,由tail查询到head持有unsafe对象,控制channel的连接,读取,写入,提供了由head到tail的直接触发事件链方法fireXXX。
3.Unsafe是操作channel的最终位置。
最后附上一个关系图,该图只有一部分,并不完全,不要完全相信:
Netty核心概念(5)之Channel的更多相关文章
- Netty In Action中文版 - 第三章:Netty核心概念
在这一章我们将讨论Netty的10个核心类.清楚了解他们的结构对使用Netty非常实用.可能有一些不会再工作中用到.可是也有一些非经常常使用也非常核心,你会遇到. Bootstrap ...
- Netty核心概念(10)之内存管理
1.前言 之前的章节已经将启动demo中能看见的内容都分析完了,Netty的一个整体样貌都在第8节线程模型最后给的图画出来了.这些内容解释了Netty为什么是一个异步事件驱动的程序,也解释了Netty ...
- Netty核心概念(8)之Netty线程模型
1.前言 第7节初步学习了一下Java原本的线程池是如何工作的,以及Future的为什么能够达到其效果,这些知识对于理解本章有很大的帮助,不了解的可以先看上一节. Netty为什么会高效?回答就是良好 ...
- Netty核心概念(6)之Handler
1.前言 本节介绍Netty中第三个重要的概念——Handler,这个在前两节都提到了,尤其是Channel和Handler联系紧密.handler本身的设计非常简单,但是所起到的作用却很大,Nett ...
- Netty核心概念(7)之Java线程池
1.前言 本章本来要讲解Netty的线程模型的,但是由于其是基于Java线程池设计而封装的,所以我们先详细学习一下Java中的线程池的设计.之前也说过Netty5被放弃的原因之一就是forkjoin结 ...
- Netty核心概念(4)之Bootstrap
1.前言 第三节介绍了Netty的一些基本概念,此节介绍Netty的第一个概念Bootstrap——启动类.Netty中服务端和客户端的启动类是不一样的,这个不要搞错了,类都在bootstrap包下. ...
- Netty核心概念
一个Netty程序始于Bootstrap类,Bootstrap类是Netty提供的一个可以通过简单配置来设置或“引导”程序的一个重要的类.Netty中设计了Handlers来处理特定的"ev ...
- Netty核心概念(9)之Future
1.前言 第7节讲解JAVA的线程模型中就说到了Future,并解释了为什么可以主线程可以获得线程池任务的执行后结果,变成一种同步状态.秘密就在于Java将所有的runnable和callable任务 ...
- 消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!
前言 本章学习,我们可以了解到以下知识点: 互联网大厂为什么选择RabbitMQ? RabbiMQ的高性能之道是如何做到的? 什么是AMQP高级协议? AMQP核心概念是什么? RabbitMQ整体架 ...
随机推荐
- Java动态代理探讨
代理模式: 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.通过代理模式,可以延迟创建对象, ...
- IntelliJ IDEA 2017版 SpringBoot的关闭自动配置和自定义Banner
一.关闭自动配置 在jar包下找下边的名字 设置关闭自动配置jar 多个的时候配置 二.自定义Banner (1)网站搜索一个图案.网址:http://patorjk.co ...
- WCF客户端第一请求server特别慢,解决办法
最近开发WCF应用的客户端,第一连接WCF后,请求数据返回的速度特别慢,不知道原因如何.最后改了下系统生成的APP.Config文件就好了,原来没有useDefaultWebProxy的选项,没有的时 ...
- hdu 4996 1~n排列LIS值为k个数
http://acm.hdu.edu.cn/showproblem.php?pid=4996 直接贴bc题解 按数字1-N的顺序依次枚举添加的数字,用2N的状态保存在那个min数组中的数字,每次新添加 ...
- SPFA穿越虫洞——负权回路得判断
poj3259 题目大意:穿越虫洞可以回到过去(时间--)所以能不能让时间倒流呢,就是判断有没有负权回路这次尝试用SPFA算法,也可以复习一下链式前向星 准备工作,队列q,spfa算法得有点就在于这个 ...
- 团队项目第六周——Alpha阶段项目复审(盐酸队)
Alpha阶段项目复审 小组 优点 缺点,bug报告 名次 天冷记得穿秋裤队 功能比较新颖,可以离线下载,做的比较完整 在下载电影时容易中断 1 只会嘤嘤嘤队 游戏和记单词的融合,也比较新颖 部分浏览 ...
- aop的概述
支付部分,定义IPayService接口并定义支付方法“pay”,并定义了两个实现:“PointPayService”表示积分支付,“RMBPayService”表示人民币支付:并且在每个支付实现中支 ...
- jquery添加select option两种代码思路比较
功能需求:在客户选择了check_in_date和check_out_date之后,将在check_in_date至check_out_date的promotions中自动添加符合条件的promoti ...
- Android SDK目录结构
Android版本下载:从4.0到8.0版本: Android SDK目录结构图: sdk全称:software develop kits 软件开发工具集 add-ons:Google API map ...
- C# Linq 学习笔记
刚刚学习了 Siki老师 的C#教程Linq部分,以下是笔记 需要引用命名空间 using System.Linq; 然后我们需要准备数据 武林高手类 /// <summary> /// ...