上一篇我们完成了对Channel的学习,这一篇让我们来学习一下ChannelFuture。

ChannelFuture的简介

ChannelFuture是Channel异步IO操作的结果。

Netty中的所有IO操作都是异步的。这意味着任何IO调用都将立即返回,而不能保证所请求的IO操作在调用结束时完成。相反,将返回一个带有ChannelFuture的实例,该实例将提供有关IO操作的结果或状态的信息。

ChannelFuture要么是未完成状态,要么是已完成状态。IO操作刚开始时,将创建一个新的Future对象。新的Future对象最初处于未完成的状态,因为IO操作尚未完成,所以既不会执行成功、执行失败,也不会取消执行。如果IO操作因为执行成功、执行失败或者执行取消导致操作完成,则将被标记为已完成的状态,并带有更多特定信息,例如失败原因。请注意,即使执行失败和取消执行也属于完成状态。

ChannelFuture提供了各种方法,可让您检查IO操作是否已完成,等待完成以及获取IO操作的结果。它还允许您添加ChannelFutureListener,以便在IO操作完成时得到通知。

Prefer addListener(GenericFutureListener) to await()

推荐使用addListener(GenericFutureListener)而不是await(),以便在完成IO操作并执行任何后续任务时得到通知。

addListener(GenericFutureListener)是非阻塞的。它只是将指定的ChannelFutureListener添加到ChannelFuture,并且与将来关联的IO操作完成时,IO线程将通知监听器。ChannelFutureListener完全不阻塞,因此可产生最佳的性能和资源利用率,但是如果不习惯事件驱动的编程,则实现顺序逻辑可能会比较棘手。

相反,await()是阻塞操作。一旦被调用,调用者线程将阻塞直到操作完成。使用await()实现顺序逻辑比较容易,但是调用者线程会不必要地阻塞直到完成IO操作为止,并且线程间通知的成本相对较高。此外,在特定情况下还可能出现死锁。

Do not call await() inside ChannelHandler

ChannelHandler中的事件处理程序方法通常由IO线程调用,如果await()是由IO线程调用的事件处理程序方法调用的,则它正在等待的IO操作可能永远不会完成,因为await()会阻塞它正在等待的IO操作,这是一个死锁。

// BAD - NEVER DO THIS
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ChannelFuture future = ctx.channel().close();
future.awaitUninterruptibly();
// Perform post-closure operation
// ...
}
// GOOD
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ChannelFuture future = ctx.channel().close();
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
// Perform post-closure operation
// ...
}
});
}

尽管有上述缺点,但是在某些情况下,调用await()更方便。在这种情况下,请确保不要在IO线程中调用await()。 否则,将引发BlockingOperationException来防止死锁。

Do not confuse I/O timeout and await timeout

使用await(long),await(long,TimeUnit),awaitUninterruptible(long)或awaitUninterruptible(long,TimeUnit)指定的timeout与IO超时根本不相关。 如果IO操作超时,则Future将被标记为“Completed with failure”,如上图所示。 例如,应通过特定于传输的选项配置连接超时:

// BAD - NEVER DO THIS
Bootstrap b = ...;
ChannelFuture f = b.connect(...);
f.awaitUninterruptibly(10, TimeUnit.SECONDS);
if (f.isCancelled()) {
// Connection attempt cancelled by user
} else if (!f.isSuccess()) {
// You might get a NullPointerException here because the future
// might not be completed yet.
f.cause().printStackTrace();
} else {
// Connection established successfully
}
   // GOOD
Bootstrap b = ...;
// Configure the connect timeout option.
b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
ChannelFuture f = b.connect(...);
f.awaitUninterruptibly(); // Now we are sure the future is completed.
assert f.isDone(); if (f.isCancelled()) {
// Connection attempt cancelled by user
} else if (!f.isSuccess()) {
f.cause().printStackTrace();
} else {
// Connection established successfully
}

ChannelFuture的方法

ChannelFuture的方法并不多,可以简单的看一下。

channel():返回ChannelFuture关联的Channel;

addListener():将指定的listener添加到Future。Future完成时,将通知指定的listener。如果Future已经完成,则立即通知指定的listener;

addListeners():和上述方法一样,只不过此方法可以新增一系列的listener;

removeListener():从Future中删除第一次出现的指定listener。完成Future时,不再通知指定的listener。如果指定的listener与此Future没有关联,则此方法不执行任何操作并以静默方式返回。

removeListeners():和上述方法一样,只不过此方法可以移除一系列的listener;

sync():等待Future直到其完成,如果这个Future失败,则抛出失败原因;

syncUninterruptibly():不会被中断的sync();

await():等待Future完成;

awaitUninterruptibly():不会被中断的await ();

isVoid():如果此ChannelFuture是void的Future,则返回true,因此不允许调用以下任何方法:

addListener(GenericFutureListener)

addListeners(GenericFutureListener[])

await()

await(long, TimeUnit) ()}

await(long) ()}

awaitUninterruptibly()

sync()

syncUninterruptibly()

为什么使用ChannelFuture?

从JDK1.5之后,J.U.C提供了Future类,它代表着异步计算的结果。Future类提供了如下方法:

方法

方法说明

boolean cancel(boolean mayInterruptIfRunning)

尝试取消执行此任务。如果任务已经完成,已经被取消或由于某些其他原因而无法取消,则此尝试将失败。如果成功,并且在调用cancel时此任务尚未启动,则该任务永远不要运行。如果任务已经启动,则mayInterruptIfRunning参数确定是否应中断执行该任务的线程以尝试停止该任务。

此方法返回后,对isDone的后续调用将始终返回true。如果此方法返回true,则随后对isCancelled的调用将始终返回true。

boolean isCancelled()

如果此任务在正常完成之前被取消,则返回true。

boolean isDone()

如果此任务完成,则返回true。完成可能是由于正常终止,异常或取消引起的,在所有这些情况下,此方法都将返回true。

V get()

必要时等待计算完成,然后检索其结果。

V get(long timeout, TimeUnit unit)

必要时最多等待给定时间以完成计算,然后检索其结果。

从这些方法中,可以看出Future类存在2大问题:

1、isDone()的定义模糊不清,不管是失败、异常还是成功,isDone()返回的都是true;

2、get()获取结果的方式是阻塞等待的方式。

所以Netty中的Future对JDK中的Future做了扩展,而ChannelFuture继承Future,固然也能充分利用这个扩展出的新特性。新特性主要体现在如下两方面:

1、引入isSuccess()来表示执行成功,引入cause()来表示执行失败的原因;

2、引入Future-Listener机制来替代主动get()阻塞等待的机制。

对于第1点,可以回到简介部分,该图表清晰的描述了这个异步调用的状态变化。当异步结果未完成时,isDone()、isSuccess()、isCancelled()均为false,同时cause()返回null,若是完成成功,则isDone()、isSuccess()均为true,若是完成失败,则isDone()为true,cause()返回not-null,若是取消完成,则

isDone()、isCancelled()均为true。可以看到新引入的特性可以很清晰的表示常用的状态。

对于第2点,Future-Listener机制本质上就是一种观察者模式,Netty中的Future通过提供addListener/addListeners方法来实现对Future执行结果的监听,一旦Future执行完成,就会触发GenericFutureListener的operationComplete方法,在该方法中就可以获取Future的执行结果,这种方式比起直接get(),能有效提升Netty的吞吐量。

至此,我们就学习完了ChannelFuture,最后总结一下:

1、Netty中的所有IO操作都是异步的。这意味着任何IO调用都将立即返回,而不能保证所请求的IO操作在调用结束时完成。ChannelFuture是Channel异步IO操作的结果;

2、ChannelFuture或者说是Future,通过引入新的特性解决了原生JDK中Future对于状态模糊不清及阻塞等待获取结果的方式,这个新特性就是引入isSuccess()、cause()方法,同时通过Future-Listener回调机制解决不知道何时能获取到Future结果的问题。

Netty:ChannelFuture的更多相关文章

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

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

  2. Netty:简单使用

    Netty是什么东西 Netty是一个封装很好的异步事件驱动框架,让我们快速的部署服务端和客户端的网络应用,进行异步IO通信. 1.什么是IO通信IO就是input 和 output,是一种在两台主机 ...

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

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

  4. Netty:Netty的介绍以及它的核心组件(二)—— ChannelFuture与回调

    Callback 回调 一个 Callback(回调)就是一个方法,一个提供给另一个的方法的引用. 这让另一个方法可以在适当的时候回过头来调用这个 callback 方法.Callback 在很多编程 ...

  5. 微言Netty:分布式服务框架

    1. 前言 几年前,我就一直想着要设计一款自己的实时通讯框架,于是出来了TinySocket,她是基于微软的SocketAsyncEventArgs来实现的,由于此类提供的功能很简洁,所以当时自己实现 ...

  6. Netty:初识Netty

    前文总结了NIO的内容,有了NIO的一些基础之后,我们就可以来看下Netty.Netty是Java领域的高性能网络传输框架,RPC的技术核心就是网络传输和序列化,所以Netty给予了RPC在网络传输领 ...

  7. Netty:Channel

    上一篇我们通过一个简单的Netty代码了解到了Netty中的核心组件,这一篇我们将围绕核心组件中的Channel来展开学习. Channel的简介 Channel代表着与网络套接字或者能够进行IO操作 ...

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

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

  9. Netty:Reactor Pattern 与 Dubbo 底层传输中的 NettyServer

    首先,我们需要了解Reactor模式的三种线程模型: 1)单线程模型 Reactor 单线程模型,指的是所有的 IO 操作都在同一个 NIO 线程上面完成,NIO 线程的职责如下: 作为 NIO 服务 ...

随机推荐

  1. ProjectTool写白包工具,秒级别写H5游戏壳包,可视化操作,极易使用,支持Swift、Objecive-C双语言

    这是自动写白包工具,秒级别写H5游戏壳包,可视化操作,极易使用,支持Swift.Objecive-C双语言 扣扣交流群:811715780 [ 官网下载 ] 这是一个白包目录示例 ProjectToo ...

  2. MySQL:GROUP_CONCAT函数的使用

    原文链接 GROUP_CONCAT功能 将某个字段的值拼接成字符串. 举例使用 先看一下原始数据表 执行下面sql语句 SELECT `cid`,GROUP_CONCAT(mid) AS `mids` ...

  3. Who Gets the Most Candies? POJ - 2886(线段树单点更新+区间查询+反素数)

    预备知识:反素数解析 思路:有了反素数的解法之后就是线段树的事了. 我们可以用线段树来维护哪些人被淘汰,哪些人没被淘汰,被淘汰的人的位置,没被淘汰的人的位置. 我们可以把所有人表示为一个[1,n]的区 ...

  4. shell脚本基础-起始句的含义

    大部分的shell脚本第一行,要么是 #!/bin/bash 要么是 #!/bin/sh 其实第二种是第一种的升级版,增加了协议posix(#!/bin/sh = #!/bin/bash + posi ...

  5. 【简说Python WEB】数据库

    目录 [简说Python WEB]数据库 数据库表 docker安装MySQL Flask-SQLAlchemy操纵MySQL数据库 初始化 定义模型 定义关系 数据库的CRUD操作 创建表 inse ...

  6. P4147 玉蟾宫 题解

    原题链接 简要题意: 求最大 \(0\) 矩阵.(将字符转化为数字) 本题是模板题,可以用来爆踩.??? 悬线法 来了! 其中绿色是 \(0\),红色是 \(1\). 下面以这个图为例讲一下算法流程. ...

  7. 曹工说Spring Boot源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎

    曹工说Spring Boot源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎 写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean De ...

  8. swift 网络请求中含有特殊字符的解决方案

    在网络请求时,URL出现中文或特殊字符时会造成请求失败,通常可使用  addingPercentEncoding(withAllowedCharacters: CharacterSet) 方法进行解决 ...

  9. Python python 函数参数:关键字参数

    # 关键字参数 '''关键字参数代表传入任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict ''' def student(name,sex,**keywords): print(' ...

  10. python实现十大经典排序算法

    Python实现十大经典排序算法 代码最后面会给出完整版,或者可以从我的Githubfork,想看动图的同学可以去这里看看: 小结: 运行方式,将最后面的代码copy出去,直接python sort. ...