简介

Channel是连接ByteBuf和Event的桥梁,netty中的Channel提供了统一的API,通过这种统一的API,netty可以轻松的对接多种传输类型,如OIO,NIO等。今天本文将会介绍Channel的使用和Channel相关的一些概念。

Channel详解

Channel是什么? Channel是一个连接网络输入和IO处理的桥梁。你可以通过Channel来判断当前的状态,是open还是connected,还可以判断当前Channel支持的IO操作,还可以使用ChannelPipeline对Channel中的消息进行处理。

先看下Channel的定义:

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {

可以看到Channel是一个接口,它继承了AttributeMap, ChannelOutboundInvoker, Comparable三个类。Comparable表示这个类可以用来做比较。AttributeMap用来存储Channel的各种属性。ChannelOutboundInvoker主要负责Channel和外部 SocketAddress 进行连接和对写。

再看下channel中定义的方法:

可以看出channel中定义的方法是多种多样的,这些方法都有些什么特点呢?接下来一一为您讲解。

异步IO和ChannelFuture

netty中所有的IO都是异步IO,也就是说所有的IO都是立即返回的,返回的时候,IO可能还没有结束,所以需要返回一个ChannelFuture,当IO有结果之后,会去通知ChannelFuture,这样就可以取出结果了。

ChannelFuture是java.util.concurrent.Future的子类,它除了可以拿到线程的执行结果之外,还对其进行了扩展,加入了当前任务状态判断、等待任务执行和添加listener的功能。

其他的功能都很好理解,它的突破在于可以对ChannelFuture添加listener,我们列出一个添加listener的方法:

Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);

添加的Listener会在future执行结束之后,被通知。不需要自己再去调用get等待future结束。这里实际上就是异步IO概念的实现,不需要主动去调用,当你完成之后来通知我就行。非常的美好!

ChannelFuture 有两个状态:uncompleted或者completed,分别代表任务的执行状态。

当一个IO刚开始的时候,返回一个ChannelFuture对象,这个对象的初始状态是uncompleted。注意,这个状态下的IO是还未开始工作的状态。当IO完成之后,不管是succeeded, failed 或者 cancelled状态,ChannelFuture的状态都会转换成为completed。

下图展示的是ChannelFuture状态和IO状态的对应图:

                                    +---------------------------+
| Completed successfully |
+---------------------------+
+----> isDone() = true |

+--------------------------+ | | isSuccess() = true |

| Uncompleted | | +=+

+--------------------------+ | | Completed with failure |

| isDone() = false | | +---------------------------+

| isSuccess() = false |----+----> isDone() = true |

| isCancelled() = false | | | cause() = non-null |

| cause() = null | | +=+

+--------------------------+ | | Completed by cancellation |

| +---------------------------+

+----> isDone() = true |

| isCancelled() = true |

+---------------------------+

如果要监控IO的状态,可以使用上面我们提到的 addListener 方法,为ChannelFuture添加一个ChannelFutureListener。

如果要等待IO执行完毕,还有一个await()方法,但是这个方法会去等待IO执行完毕,是一个同步的方法,所以并不推荐。

相比而言,addListener(GenericFutureListener)是一个非阻塞的异步方法,将会把一个ChannelFutureListener添加到ChannelFuture中,当IO结束之后会自动通知ChannelFutureListener,非常好用。

对于处理IO操作的ChannelHandler来说,为了避免IO的阻塞,一定不要在ChannelHandler的IO方法中调用await(),这样有可能会导致ChannelHandler因为IO阻塞导致性能下降。

下面举两个例子,一个是错误的操作,一个是正确的操作:

   // 错误操作
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ChannelFuture future = ctx.channel().close();
future.awaitUninterruptibly();
// 调用其他逻辑
} // 正确操作
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ChannelFuture future = ctx.channel().close();
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
// 调用其他逻辑
}
});
}

大家可以对比下上面两种写法的区别。

另外要注意的是ChannelFuture中的这些await方法比如:await(long), await(long, TimeUnit), awaitUninterruptibly(long), 或者 awaitUninterruptibly(long, TimeUnit)可以带一个过期时间,大家要注意的是这个过期时间是等待IO执行的时间,并不是IO的timeout时间,也就是说当await超时之后,IO还有可能没有执行完成,这就导致了下面的代码有可能报错:

   Bootstrap b = ...;
ChannelFuture f = b.connect(...);
f.awaitUninterruptibly(10, TimeUnit.SECONDS);
if (f.isCancelled()) {
// 用户取消了Channel
} else if (!f.isSuccess()) {
// 这里可能会报异常,因为底层的IO可能还没有执行完成
f.cause().printStackTrace();
} else {
// 成功建立连接
}

上面的代码可以改成下面的例子:

  Bootstrap b = ...;
// 配置连接timeout的时间
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
ChannelFuture f = b.connect(...);
f.awaitUninterruptibly(); // 等待直到底层IO执行完毕
assert f.isDone(); if (f.isCancelled()) {
// 用户手动取消Channel
} else if (!f.isSuccess()) {
f.cause().printStackTrace();
} else {
// 成功建立连接
}

Channel的层级结构

netty中的Channel是有层级结构的,通过parent属性可获取这种层级结构。parent获取的对象和Channel的创建方式有关。比如如果是一个被ServerSocketChannel accepted的SocketChannel,那么它的parent就是ServerSocketChannel。

释放资源

和所有的IO一样,Channel在用完之后也需要被释放,需要调用close()或者close(ChannelPromise) 方法。

事件处理

channel负责建立连接,建立好的连接就可以用来处理事件ChannelEvent了,实际上ChannelEvent是由定义的一个个Channelhandler来处理的。而ChannelPipeline就是连接channel和channelhandler的桥梁。

我们将会下下一章详细讲解ChannelEvent、Channelhandler和ChannelPipeline的关联关系,敬请期待。

总结

Channel在netty中是做为一个关键的通道而存在的,后面的Event和Handler是以channel为基础运行的,所以说Channel就是netty的基础,好了,今天的介绍到这里就结束了,敬请期待后续的文章。

本文已收录于 http://www.flydean.com/04-netty-channel/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:netty中的Channel详解的更多相关文章

  1. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  2. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  3. Android中的动画详解系列【4】——Activity之间切换动画

    前面介绍了Android中的逐帧动画和补间动画,并实现了简单的自定义动画,这一篇我们来看看如何将Android中的动画运用到实际开发中的一个场景--Activity之间跳转动画. 一.定义动画资源 如 ...

  4. Java 8系列之Stream的基本语法详解

    本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...

  5. OSGi 系列(三)之 bundle 详解

    OSGi 系列(三)之 bundle 详解 1. 什么是 bundle bundle 是以 jar 包形式存在的一个模块化物理单元,里面包含了代码,资源文件和元数据(metadata),并且 jar ...

  6. Python操作redis系列以 哈希(Hash)命令详解(四)

    # -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1. Hset 命令用于 ...

  7. [翻译]Unity中的AssetBundle详解(三)

    构建AssetBundles 在AssetBundle工作流程的文档中,我们有一个示例代码,它将三个参数传递给BuildPipeline.BuildAssetBundles函数.让我们更深入地了解我们 ...

  8. 「翻译」Unity中的AssetBundle详解(二)

    为AssetBundles准备资源 使用AssetBundles时,您可以随意将任何Asset分配给所需的任何Bundle.但是,在设置Bundles时,需要考虑一些策略.这些分组策略可以使用到任何你 ...

  9. 「翻译」Unity中的AssetBundle详解(一)

    AssetBundles AssetBundle是一个存档文件,其中包含平台在运行时加载的特定资产(模型,纹理,预制,音频剪辑,甚至整个场景).AssetBundles可以表示彼此之间的依赖关系;例如 ...

随机推荐

  1. 京东 Vue3 组件库支持小程序开发啦!

    源码抢先看: https://github.com/jdf2e/nutui NutUI 3.0 官网:https://nutui.jd.com/3x/#/ 小程序多端适配 设计初衷 在跨端小程序的开发 ...

  2. 一个SQLServer中JSON文档型数据的查询问题

    近日在项目中遇到一个问题: 如何在报表中统计JSON格式存储的数据? 例如有个调查问卷记录表,记录每个问题的答案. 其结构示意如下(横表设计) Id user date Q1_Answer Q2_An ...

  3. canvas小画板——(3)笔锋效果

    画线准备 准备一个canvas <canvas id="canvasId" width="1000" height="800"> ...

  4. kerberos认证协议爱情故事

    0x01.kerberos简介 kerberos是一种域内认证协议,Kerberos的标志是三头狗,狗头分别代表以下角色: Client Server KDC(Key Distribution Cen ...

  5. Linux中cut,sort,uniq和wc的用法

    一.cut是一个选取命令,就是将一段数据经过分析,取出我们想要的.一般来说,选取信息通常是针对"行"来进行分析的,并不是整篇信息分析的.1.语法格式为:cut [-bn] [fil ...

  6. Vue 前端权限控制的优化改进版

    1.前言   之前<Vue前端访问控制方案 >一文中提出,使用class="permissions"结合元素id来标识权限控制相关的dom元素,并通过公共方法check ...

  7. 7、基本数据类型(tuple)

    7.1.tuple类: 1.元组元素用小括号括起来,用逗号分割每个元素,一般写元组的时候,推荐在最后加入逗号,该 逗号不占元素位置,目的是为了方便识别: tu = (111, "alex&q ...

  8. 什么是 Shell 脚本?

    Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务.Windows Explorer 是一个典型的图形 ...

  9. centos 8 chown命令详解

    chown命令简介 chown将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户ID:组可以是组名或者组ID: 文件是以空格分开的要改变权限的文件列表,支持通配符. 系统管理员经常使用ch ...

  10. margin属性总结,你想知道的这里都有

    一.前言 在学习CSS时,遇到的很多问题都是和margin有关,这个小怪兽总是出其不意的让我的界面排版变的混乱,还让人摸不着头脑,原因还是在于我对他的一些属性没有进行一个深入的了解,导致我在设计之初就 ...