Netty核心组件之ChannlFuture


ChannelFuture是Channel的异步I/O操作的结果

在Netty中,所有的I/O操作都是异步的。这就意味着,任何I/O调用都将会立即返回,但是不保证在I/O调用结束时,这个I/O操作的请求已经完成。
不过,它会返回一个ChannelFuture实例给你,里面提供了这个I/O操作的结果或者状态。

一个ChannelFuture的状态不是uncompleted(
未完成的)就是completed(已完成)。
当开始一个I/O操作时,一个新的ChannelFuture对象就会被创建。这个对象初始化的状态是uncompleted(未完成)--它既不是
succeeded(成功),failed(失败),也不是cancelled(已取消),因为当前I/O操作还没有完成。当I/O操作完成之后,当前ChannelFuture对象就会被
标记上详细的信息:成功、失败或者取消 的具体原因。 请注意:失败 和 取消 两种状态属于已完成的状态。 +---------------------------+ | 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 | +---------------------------+

ChannelFuture中提供了多种方法,以便于 检查当前I/O操作是否已完成、等待当前操作完成、检索当前操作的结果。
同时,它也允许你添加ChannelFutureListener监听器,这样在I/O操作完成时你就可以收到通知。

注意:首选#addListener()方法,而不是#await()方法

无论何时,当你想得到I/O操作完成时的通知时并做接下来的操作的时候,推荐的做法是选择#addListener()方法,而不是#await()方法

#addListener()方法是非阻塞的,它只是简单的将具体的ChannelFutureListener添加到当前的ChannelFuture中,
并且当与当前ChannelFuture关联的I/O操作完成时,I/O线程将会通知这些监听器。由于ChannelFutureListener完全不阻塞,
所以它有最好的性能和资源利用率,但是,如果您不习惯事件驱动的编程,那么实现顺序逻辑可能会很棘手

相比之下,#await()是阻塞操作。一旦调用,调用者线程将阻塞,直到操作完成为止。
使用#await()实现顺序逻辑比较容易,但是调用者线程会不必要地阻塞,直到完成I/O操作为止,并且线程间通知的开销也相对较高。
此外,在特定情况下有死锁的可能性,如下所述:

请勿在ChannelHandler中调用#await()
ChannelHandler中的事件处理程序方法通常是由一个I/O线程调用的。
如果#await()由事件处理程序方法(由I/O线程调用)调用,则它正在等待的I/O操作可能永远不会完成,因为#await()可以阻止正在等待的I/O 操作,这是一个死锁。
永远不要这样做: {
@code @Override} public void channelRead({@link ChannelHandlerContext} ctx, Object msg) { {@link ChannelFuture} future = ctx.channel().close(); future.awaitUninterruptibly(); // Perform post-closure operation // ... } 可以这样做: {@code @Override} public void channelRead({@link ChannelHandlerContext} ctx, Object msg) { {@link ChannelFuture} future = ctx.channel().close(); future.addListener(new {@link ChannelFutureListener}() { public void operationComplete({@link ChannelFuture} future) { // Perform post-closure operation // ... } }); } 尽管存在上述缺点,但是在某些情况下调用#await()更方便。
在这种情况下,请确保不要在I/O线程中调用#await()。否则,将产生BlockingOperationException异常以防止死锁
不要混淆I/O超时和等待超时

您使用#await(long),#await(long,TimeUnit),#awaitUninterruptible(long)或#awaitUninterruptible(long)指定的超时值,TimeUnit)与I/O 超时完全无关。
如果I/O操作超时,则将来将被标记为“completed with failure(失败完成)”,如上图所示。例如,连接超时应通过特定于传输的选项进行配置:
永远不要这样做:
Bootstrap b = ...; ChannelFuture f = b.connect(...); f.awaitUninterruptibly(10, TimeUnit.SECONDS); if (f.isCancelled()) { // 用户尝试取消连接 } else if (!f.isSuccess()) { // 这里可能产生一个NullPointerException // 因为future可能还没有完成 f.cause().printStackTrace(); } else { // 成功建立连接 } 可以这样做: Bootstrap b = ...; // 配置连接超时选项 b.option({ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) ChannelFuture f = b.connect(...); f.awaitUninterruptibly(); // 现在我们可以确定任务完成了 assert f.isDone(); if (f.isCancelled()) { //用户尝试取消连接 } else if (!f.isSuccess()) { f.cause().printStackTrace(); } else { // 成功建立连接 }

 

Netty核心组件之ChannlFuture

上一篇:理解Windows中的路由表和默认网关


下一篇:WPF通过代码动态的加载样式