netty理论之源码分析⑦ 细讲future和Channelfuture

 

一、future和Channelfuture分别是啥?

Future最早出现于JDK的java.util.concurrent。Future,它用于表示异步操作的结果。在Netty中所有的IO操作都是异步的,因此,你不能立刻得知消息是否被正确处理,但是我们可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过Future和ChannelFuture,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个ChannelFuture。由于Netty的Future都是与异步I/O操作相关的,因此命名为ChannelFuture,代表它与Channel操作相关。

1.从源码入手

在介绍Channelfuture之前,先来读一下它主要的源码

package io.netty.channel;
 
import io.netty.bootstrap.Bootstrap;
import io.netty.util.concurrent.BlockingOperationException;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
import java.util.concurrent.TimeUnit;
 
 
/**
 * The result of an asynchronous {@link Channel} I/O operation.
 * 一个异步的针对于Channel的IO操作的结果
 * 
 * <p>
 * All I/O operations in Netty are asynchronous.  It means any I/O calls will
 * return immediately with no guarantee that the requested I/O operation has
 * been completed at the end of the call.  Instead, you will be returned with
 * a {@link ChannelFuture} instance which gives you the information about the
 * result or status of the I/O operation.
 * 在Netty中,所有的IO操作都是异步的。划重点
 * 在这就意味着任何的Io调用都会立刻返回,但是却不保证这个IO操作在调用结束之后是已完成的。
 * 相反,你会得到一个ChannelFuture对象,这个对象携带这Io操作的结果或者状态信息。
 * 
 * <p>
 * A {@link ChannelFuture} is either <em>uncompleted</em> or <em>completed</em>.
 * When an I/O operation begins, a new future object is created.  The new future
 * is uncompleted initially - it is neither succeeded, failed, nor cancelled
 * because the I/O operation is not finished yet.  If the I/O operation is
 * finished either successfully, with failure, or by cancellation, the future is
 * marked as completed with more specific information, such as the cause of the
 * failure.  Please note that even failure and cancellation belong to the
 * completed state.
 * ChannelFuture是未完成的或者完成的。。
 * 当一个IO操作开始的时候,一个新的Future对象将会被创建。这个新的future是最开始是未完成的,它既不是成功的,失败的,也不是被取消的,
 *以为Io操作到目前还没有结束。
 *如果这个IO操作完成,不管是成功的,失败的,还是取消操作的,这个future都会被标记为完成,它会携带一些具体的信息,例如失败的原因等等。
 *请注意,即使是失败和取消都属于完成的状态。
 *下面是示意图
 * <pre>
 *                                      +---------------------------+
 *                                      | 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      |
 *                                      +---------------------------+
 * </pre>
 *
 * Various methods are provided to let you check if the I/O operation has been
 * completed, wait for the completion, and retrieve the result of the I/O
 * operation. It also allows you to add {@link ChannelFutureListener}s so you
 * can get notified when the I/O operation is completed. 
 * 提供了各种各样的方法让你检查IO操作是否已经完成,等待完成,获取IO操作结果。他也允许你添加ChannelFutureListener,
 * 让你能够在IO操作完成之后收到通知
 * 
 * <h3>Prefer {@link #addListener(GenericFutureListener)} to {@link #await()}</h3>
 * 应该使用addListener(GenericFutureListener)这样的方式去获取结果,而不是使用await()方法。
 * 
 * It is recommended to prefer {@link #addListener(GenericFutureListener)} to
 * {@link #await()} wherever possible to get notified when an I/O operation is
 * done and to do any follow-up tasks.
 * 只要IO操作完成,它就推荐使用addListener(GenericFutureListener)来获得通知,然后执行下面的任务。
 * <p>
 * {@link #addListener(GenericFutureListener)} is non-blocking.  It simply adds
 * the specified {@link ChannelFutureListener} to the {@link ChannelFuture}, and
 * I/O thread will notify the listeners when the I/O operation associated with
 * the future is done.  {@link ChannelFutureListener} yields the best
 * performance and resource utilization because it does not block at all, but
 * it could be tricky to implement a sequential logic if you are not used to
 * event-driven programming.
 * addListener(GenericFutureListener)是非阻塞的。可以非常简单的添加到ChannelFuture中,
 * 当与这个future相关的Io操作完成之后,IO线程将会通知这些监听器。
 * 使用ChannelFutureListener会有最好的性能和资源利用的表现,因为它是完全不阻塞的。
 * 但是它对于实现一个顺序逻辑是不太友好的,如果你没有使用过事件驱动编程的话
 * <p>
 * By contrast, {@link #await()} is a blocking operation.  Once called, the
 * caller thread blocks until the operation is done.  It is easier to implement
 * a sequential logic with {@link #await()}, but the caller thread blocks
 * unnecessarily until the I/O operation is done and there's relatively
 * expensive cost of inter-thread notification.  Moreover, there's a chance of
 * dead lock in a particular circumstance, which is described below.
 * 相比而言,await()是一个阻塞的操作,一旦被调用,这个调用线程将会一直阻塞,知道操作完成。
 * 它很容易就能够实现顺序逻辑,但是调用者线程会进行没有必要的阻塞,直到io操作完成,所在在夸线程通信当中,这里会产生相对昂贵的成本。
 * 更重要的是,在一个特殊的环境下,会给产生死锁提供一个机会。
 * <h3>Do not call {@link #await()} inside {@link ChannelHandler}</h3>
 * <p>不要在ChannelHandler中调用await()方法。
 * The event handler methods in {@link ChannelHandler} are usually called by
 * an I/O thread.  If {@link #await()} is called by an event handler
 * method, which is called by the I/O thread, the I/O operation it is waiting
 * for might never complete because {@link #await()} can block the I/O
 * operation it is waiting for, which is a dead lock.
 * 在ChannelHandle中的事件处理器方法通常会被一个Io线程所调用,如果await()方法被一个事件处理方法调用,然后这个事件处理方法又被IO线程调用,
 * 那么这个IO线程就会一直等待,也许再也不会结束。因为await在等待着Io线程结束,这样就产生了一个死锁
 * <pre>
 * // BAD - NEVER DO THIS
 * 坏的写法,永远也不要这么做
 * {@code @Override}
 * public void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
 *     {@link ChannelFuture} future = ctx.channel().close();
 *     future.awaitUninterruptibly();//调用了await方法,等待Io线程执行完毕,而io线程却在等待ChannelHandler的事件方法执行完毕,死锁就产生了。
 *     // Perform post-closure operation
 *     // ...
 * }
 *
 * // GOOD
 * {@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
 *             // ...
 *         }
 *     });
 * }
 * </pre>
 * <p>
 * In spite of the disadvantages mentioned above, there are certainly the cases
 * where it is more convenient to call {@link #await()}. In such a case, please
 * make sure you do not call {@link #await()} in an I/O thread.  Otherwise,
 * {@link BlockingOperationException} will be raised to prevent a dead lock.
 * 如上所述,尽管有这些劣势,但是当然也有一些调用await更加方便的情况,
 * 这样的情况下,请确定你没有在一个IO线程中调用await,否则将会抛出异常来阻塞死锁的发生
 *
 * <h3>Do not confuse I/O timeout and await timeout</h3>
 * 不要混淆IO超时跟等待超时。
 *
 * The timeout value you specify with {@link #await(long)},
 * {@link #await(long, TimeUnit)}, {@link #awaitUninterruptibly(long)}, or
 * {@link #awaitUninterruptibly(long, TimeUnit)} are not related with I/O
 * timeout at all.  If an I/O operation times out, the future will be marked as
 * 'completed with failure,' as depicted in the diagram above.  For example,
 * connect timeout should be configured via a transport-specific option:
 * 等待超时跟io超时完全没有关系。如果一个IO操作超时,这个future将会被用失败标记为完成。就像上面的图描述的那样,
 * 例如连接超时时间应该通过一个特定的传输选项进行配置。
 * <pre>
 * // BAD - NEVER DO THIS
 * {@link Bootstrap} b = ...;
 * {@link 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.
 *     //在这个地方可能会抛出一个空指针异常,因为这个future还没有被标记为完成。
 *     f.cause().printStackTrace();
 * } else {
 *     // Connection established successfully
 * }
 *
 * // GOOD
 * {@link Bootstrap} b = ...;
 * // Configure the connect timeout option.
 * <b>b.option({@link ChannelOption}.CONNECT_TIMEOUT_MILLIS, 10000);</b>
 * IO超时中的连接超时
 * {@link ChannelFuture} f = b.connect(...);
 * f.awaitUninterruptibly();
 * 
 * // Now we are sure the future is completed.
 * assert f.isDone();//io超时后,future就会被标记成done了。
 *
 * if (f.isCancelled()) {
 *     // Connection attempt cancelled by user
 * } else if (!f.isSuccess()) {
 *     f.cause().printStackTrace();
 * } else {
 *     // Connection established successfully
 * }
 * </pre>
 */
public interface ChannelFuture extends Future<Void> {
 
    /**
     * Returns a channel where the I/O operation associated with this
     * future takes place.
     */
    Channel channel();
 
    @Override
    ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> listener);
 
    @Override
    ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
 
    @Override
    ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> listener);
 
    @Override
    ChannelFuture removeListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
 
    @Override
    ChannelFuture sync() throws InterruptedException;
 
    @Override
    ChannelFuture syncUninterruptibly();
 
    @Override
    ChannelFuture await() throws InterruptedException;
 
    @Override
    ChannelFuture awaitUninterruptibly();
 
    /**
     * Returns {@code true} if this {@link ChannelFuture} is a void future and so not allow to call any of the
     * following methods:
     * <ul>
     *     <li>{@link #addListener(GenericFutureListener)}</li>
     *     <li>{@link #addListeners(GenericFutureListener[])}</li>
     *     <li>{@link #await()}</li>
     *     <li>{@link #await(long, TimeUnit)} ()}</li>
     *     <li>{@link #await(long)} ()}</li>
     *     <li>{@link #awaitUninterruptibly()}</li>
     *     <li>{@link #sync()}</li>
     *     <li>{@link #syncUninterruptibly()}</li>
     * </ul>
     */
    boolean isVoid();
}

从源码可以读出,在介绍完netty的Channelfuture之后,接着介绍了Channelfuture的状态

ChannelFuture有两种状态:未完成(uncompleted)和完成(completed). 
当令Channel开始一个I/O操作时,会创建一个新的ChannelFuture去异步完成操作. 
被创建时的ChannelFuture处于uncompleted状态(非失败,非成功,非取消);一旦ChannelFuture完成I/O操作,ChannelFuture将处于completed状态,结果可能有三种: 

  • 操作成功 
  • 操作失败 
  • 操作被取消(I/O操作被主动终止)
ChannelFuture有两种状态:未完成(uncompleted)和完成(completed). 
当令Channel开始一个I/O操作时,会创建一个新的ChannelFuture去异步完成操作. 
被创建时的ChannelFuture处于uncompleted状态(非失败,非成功,非取消);一旦ChannelFuture完成I/O操作,ChannelFuture将处于completed状态,结果可能有三种: 
1. 操作成功 
2. 操作失败 
3. 操作被取消(I/O操作被主动终止)

GenericFutureListener监听接口

虽然可以通过ChannelFutureget()方法获取异步操作的结果,但完成时间是无法预测的,若不设置超时时间则有可能导致线程长时间被阻塞;若是不能精确的设置超时时间则可能导致I/O操作中断.因此,Netty建议通过GenericFutureListener接口执行异步操作结束后的回调.

Netty API 中使用的GenericFutureListener示例代码:

 @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
             // ...
         }
     });
 }

另外,ChannelFuture允许添加一个或多个(移除一个或多个)GenericFutureListener监听接口,方法名:addListener()addListeners()removeListener()removeListeners().

推荐使用addListener(ChannelFutureListener)异步得到通知当一个I / O操作完成后,做任何后续任务,而不是通过调用await方法(降低吞吐量)。但如果你想要业务场景是必须先执行A,然后同步执行B(异步通知不合适的场景),使用await是比较方便的。但await有一个限制,调用await方法的线程不能是I/O 线程(work线程),否则会抛出一个异常,避免死锁

维持netty中Future的生命周期的方法

  • sync()
  • syncUninterruptibly()
  • await()
  • await(long timeout, TimeUnit unit)
  • awaitUninterruptibly(long timeout, TimeUnit unit):
  • awaitUninterruptibly(long timeoutMillis);

二,实例实战代码

1.服务器端

package hello.netty.yxf.com;
 
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
 
public class HelloServer {
    public void start(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            // 注册handler
                            ch.pipeline().addLast(new HelloServerInHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            long t1 = System.currentTimeMillis();
            ChannelFuture f = b.bind(port).sync();
            f.channel().closeFuture().sync();
            long t2 = System.currentTimeMillis();
            System.out.print("diff in seconds:" + (t2 - t1) / 1000 + "\n");
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
 
    public static void main(String[] args) throws Exception {
        HelloServer server = new HelloServer();
        server.start(9090);
    }
}




==============================================================




package hello.netty.yxf.com;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
 
// 该handler是InboundHandler类型
public class HelloServerInHandler extends ChannelInboundHandlerAdapter {
    @Override
    public boolean isSharable() {
        System.out.println("==============handler-sharable==============");
        return super.isSharable();
    }
 
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel-register==============");
    }
 
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel-unregister==============");
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel-active==============");
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel-inactive==============");
    }
 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 
        System.out.println("==============channel-read==============");
 
        ByteBuf result = (ByteBuf) msg;
        byte[] result1 = new byte[result.readableBytes()];
        // msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中
        result.readBytes(result1);
        String resultStr = new String(result1);
        // 接收并打印客户端的信息
        System.out.println("Client said:" + resultStr);
        // 释放资源,这行很关键
        result.release();
 
        // 向客户端发送消息
        String response = "I am ok!";
        // 在当前场景下,发送的数据必须转换成ByteBuf数组
        ByteBuf encoded = ctx.alloc().buffer(4 * response.length());
        encoded.writeBytes(response.getBytes());
        ctx.writeAndFlush(encoded);
        Thread.sleep(10000);
        System.out.println("thread sleep end");
 
        ctx.close();
 
//        Thread.sleep(10000);
//        System.out.println("thread sleep end");
    }
 
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel-read-complete==============");
        ctx.flush();
    }
}

2.客户端实例

package hello.netty.yxf.com;
 
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
 
/**
 * 1、Client向Server发送消息:Are you ok?
 * 2、Server接收客户端发送的消息,并打印出来。
 * 3、Server端向客户端发送消息:I am ok!
 * 4、Client接收Server端发送的消息,并打印出来,通讯结束。
 */
 
public class HelloClient {
    public void connect(String host, int port) throws Exception {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
 
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new HelloClientIntHandler());
                }
            });
 
            // Start the client.
            /**
             * wait()方法:Waits for this future to be completed.
             * Waits for this future until it is done, and rethrows the cause of the failure if this future
             * failed.
             */
            long t1 = System.currentTimeMillis();
            ChannelFuture f = b.connect(host, port).await();
            // Wait until the connection is closed.
            f.channel().closeFuture().await();    //closeFuture方法返回通道关闭的结果
            long t2 = System.currentTimeMillis();
            System.out.print("diff in seconds:" + (t2 - t1) / 1000 + "\n");
        } finally {
            workerGroup.shutdownGracefully();
        }
 
    }
 
    public static void main(String[] args) throws Exception {
        HelloClient client = new HelloClient();
        client.connect("127.0.0.1", 9090);
    }
}



==============================================


package hello.netty.yxf.com;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
 
 
//InboundHandler类型
public class HelloClientIntHandler extends ChannelInboundHandlerAdapter {
 
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel--register==============");
    }
 
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel--unregistered==============");
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel--inactive==============");
    }
 
    // 连接成功后,向server发送消息
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("==============channel--active==============");
        String msg = "Are you ok?";
        /**
         * 分配ByteBuf
         * Return the assigned {@link io.netty.buffer.ByteBufAllocator} which will be used to allocate {@link ByteBuf}s.
         */
        ByteBuf encoded = ctx.alloc().buffer(4 * msg.length());
        encoded.writeBytes(msg.getBytes());
        ctx.write(encoded);
        ctx.flush();
    }
 
    // 接收server端的消息,并打印出来
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("==============channel--read==============");
        //先等待两秒
        Thread.sleep(2000);
        ByteBuf result = (ByteBuf) msg;
        byte[] result1 = new byte[result.readableBytes()];
        result.readBytes(result1);
        System.out.println("Server said:" + new String(result1));
        result.release();
    }
}

客户端的异步IO

让这个demo异步方式运行则客户端的代码应该是这样的:

long t1 = System.currentTimeMillis();
ChannelFuture f = b.connect(host, port).await();
long t2 = System.currentTimeMillis();
System.out.print("diff in seconds:" + (t2 - t1) / 1000 + "\n");

运行结果如下

==============channel–register==============

diff in seconds:0

==============channel–active==============

==============channel–inactive==============

==============channel–unregistered==============

和原来的代码相比,通过运行结果可以分析出没有read服务器的数据。

在看一段异步的代码:

   long t1 = System.currentTimeMillis();
            ChannelFuture f = b.connect(host, port).await();
            f = f.channel().closeFuture();
            f.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("success complete!!ok!!");
                }
            });
            long t2 = System.currentTimeMillis();
            System.out.print("diff in seconds:" + (t2 - t1) / 1000 + "\n");

运行结果

==============channel–register==============

==============channel–active==============

diff in seconds:0

success complete!!ok!!

==============channel–inactive==============

==============channel–unregistered==============

给通道的关闭Future注册了监听事件,监听事件等这个关闭Future完成后打印了字符串,而客户端没有读取服务器的数据。

再看一段代码

            long t1 = System.currentTimeMillis();
            ChannelFuture f = b.connect(host, port).await();
            f = f.channel().closeFuture().await();
            f.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("success complete!!ok!!");
                }
            });
            long t2 = System.currentTimeMillis();
            System.out.print("diff in seconds:" + (t2 - t1) / 1000 + "\n");

运行结果: 

==============channel–register==============

==============channel–active==============

==============channel–read==============

Server said:I am ok!

==============channel–inactive==============

==============channel–unregistered==============

diff in seconds:2

success complete!!ok!!

可以读取服务器的数据,并且监听事件也起了作用,但这不是一个异步调用。

总结
      netty的异步结果Future继承于JUC的Future,可以异步获取IO操作的结果信息,比如IO操作是否成功完成,如果失败,可以获取失败的原因,是否取消,同时可以使用cancel方法取消IO操作,添加异步结果监听器,、监听IO操作是否完成,并可以移除结果监听器,除这些之外我们还可以异步、同步等待或超时等待IO操作结果。
      异步结果监听器GenericFutureListener,主要监听一个IO操作是否完成,在异步结果有返回值时,通知监听器。
      ChannelFuture继承于空异步结果,即没有返回值,所以添加移除监听器,同步异步等待方法为空体。netty所有的IO操作都是异步的,当一个IO操作开始时,不管操作是否完成,一个新的异步操作结果将会被创建。如果因为IO操作没有完成,同时既没有成功,失败,也没有取消,新创建的那么,异步结果并没有完成初始化。如果IO操作完成,不论操作结果成功,失败或取消,异步结果将会标记为完成,同时携带更多的精确信息,比如失败的原因。需要注意的时,失败或取消也属于完成状态。强烈建议使用添加监听器的方式等待IO操作结果,而不await方法,因为监听器模式时非阻塞的,有更好的性能和资源利用率。
      通道结果监听器ChannelFutureListener内部有3个监听器,分别为在操作完成时,关闭通道任务关联的通道的监听器CLOSE;当IO操作失败时,关闭通道任务关联的通道的监听器CLOSE_ON_FAILURE;转发通道任务异常到Channel管道的监听器FIRE_EXCEPTION_ON_FAILURE。
       Promise任务继承了任务Future,但多了以便标记成功、失败和不可取消的方法。
ChannelPromise与ChannelFuture的不同在于ChannelPromise可以标记任务结果。
ChannelProgressivePromise与ProgressivePromise,ChannelProgressiveFuture的关系与ChannelPromise与Promise,ChannelFuture的关系类似,只不过ChannelPromise表示异步操作任务,ChannelProgressivePromise表示异步任务的进度,同时Promise类型异步任务都是可写的

猜你喜欢

转载自blog.csdn.net/weixin_39666581/article/details/81192954
今日推荐