欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Netty:ChannelFuture

程序员文章站 2022-05-04 12:05:24
上一篇我们完成了对Channel的学习,这一篇让我们来学习一下ChannelFuture。 ChannelFuture的简介 ChannelFuture是Channel异步IO操作的结果。 Netty中的所有IO操作都是异步的。这意味着任何IO调用都将立即返回,而不能保证所请求的IO操作在调用结束时 ......

我们完成了对channel的学习,这一篇让我们来学习一下channelfuture。

channelfuture的简介

channelfuture是channel异步io操作的结果。

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

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

Netty:ChannelFuture

 

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的方法

Netty: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结果的问题。