Netty 源码分析(三)

来源:互联网 发布:java isinteger 编辑:程序博客网 时间:2024/05/19 22:51

我们来看ServerBootstrap.bind()  方法 

bind方法 位于父类AbstractBootstrap中

 /**     * Create a new {@link Channel} and bind it.     */    public ChannelFuture bind(int inetPort) {        return bind(new InetSocketAddress(inetPort));    }

 /**     * Create a new {@link Channel} and bind it.     */    public ChannelFuture bind(SocketAddress localAddress) {        validate();        if (localAddress == null) {            throw new NullPointerException("localAddress");        }        return doBind(localAddress);    }
 验证方法 并没有什么,对 之前的赋值 进行校验。
 /**     * Validate all the parameters. Sub-classes may override this, but should     * call the super method in that case.     */    @SuppressWarnings("unchecked")    public B validate() {        if (group == null) {            throw new IllegalStateException("group not set");        }        if (channelFactory == null) {            throw new IllegalStateException("channel or channelFactory not set");        }        return (B) this;    }
我们在看 doBind方法: 里面有个初始化并注册的方法initAndRegister  ,看这个之前我们在看看 ChannelFuture ,我们可以看出ChannelFuture继承了Netty的Future ,Netty的Future 又继承了 jdk1.5里的concurrent.Future 

public interface ChannelFuture extends Future<Void> 
public interface Future<V> extends java.util.concurrent.Future<V>

private ChannelFuture doBind(final SocketAddress localAddress) {        final ChannelFuture regFuture = initAndRegister();        final Channel channel = regFuture.channel();        if (regFuture.cause() != null) {            return regFuture;        }        if (regFuture.isDone()) {            // At this point we know that the registration was complete and successful.            ChannelPromise promise = channel.newPromise();            doBind0(regFuture, channel, localAddress, promise);            return promise;        } else {            // Registration future is almost always fulfilled already, but just in case it's not.            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);            regFuture.addListener(new ChannelFutureListener() {                @Override                public void operationComplete(ChannelFuture future) throws Exception {                    Throwable cause = future.cause();                    if (cause != null) {                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an                        // IllegalStateException once we try to access the EventLoop of the Channel.                        promise.setFailure(cause);                    } else {                        // Registration was successful, so set the correct executor to use.                        // See https://github.com/netty/netty/issues/2586                        promise.registered();                        doBind0(regFuture, channel, localAddress, promise);                    }                }            });            return promise;        }    }

我们来看initAndRegister

final ChannelFuture initAndRegister() {        Channel channel = null;        try {            channel = channelFactory.newChannel();            init(channel);        } catch (Throwable t) {            if (channel != null) {                // channel can be null if newChannel crashed (eg SocketException("too many open files"))                channel.unsafe().closeForcibly();            }            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);        }        ChannelFuture regFuture = config().group().register(channel);        if (regFuture.cause() != null) {            if (channel.isRegistered()) {                channel.close();            } else {                channel.unsafe().closeForcibly();            }        }        // If we are here and the promise is not failed, it's one of the following cases:        // 1) If we attempted registration from the event loop, the registration has been completed at this point.        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.        // 2) If we attempted registration from the other thread, the registration request has been successfully        //    added to the event loop's task queue for later execution.        //    i.e. It's safe to attempt bind() or connect() now:        //         because bind() or connect() will be executed *after* the scheduled registration task is executed        //         because register(), bind(), and connect() are all bound to the same thread.        return regFuture;    }

  还记得channelFactory 是通过ReflectiveChannelFactory 

public T newChannel() {          try {              return clazz.getConstructor().newInstance();          } catch (Throwable t) {              throw new ChannelException("Unable to create Channel from class " + clazz, t);          }      }

通过反射调用NioServerSocketChannel无参的构造方法创建NioServerSocketChannel 的实例

我们在看 NioServerSocketChannel : 是如何创建的 默认构造方法NioServerSocketChannel()里的this方法调用newSocket 方法


public class NioServerSocketChannel extends AbstractNioMessageChannel                             implements io.netty.channel.socket.ServerSocketChannel {    private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();    private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);    private static ServerSocketChannel newSocket(SelectorProvider provider) {        try {            /**             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.             *             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.             */            return provider.openServerSocketChannel();        } catch (IOException e) {            throw new ChannelException(                    "Failed to open a server socket.", e);        }    }    private final ServerSocketChannelConfig config;    /**     * Create a new instance     */    public NioServerSocketChannel() {        this(newSocket(DEFAULT_SELECTOR_PROVIDER));    }

我们看下 concurrent.Future 

一个future 代表一个异步计算的结果,并且提供了一些方法来检查计算是否已经完成,还提供了 等待计算完成方法,提供了计算完成的结果,计算完成的结果 只能通过get方法获取到,get方法默认是阻塞的 ,直到计算完成 才返回计算结果。取消操作是通过cancle方法来完成,还提供了一些方法来判断 计算是正常完成的还是被取消掉的,

/* * * * * * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */package java.util.concurrent;/** * A {@code Future} represents the result of an asynchronous * computation.  Methods are provided to check if the computation is * complete, to wait for its completion, and to retrieve the result of * the computation.  The result can only be retrieved using method * {@code get} when the computation has completed, blocking if * necessary until it is ready.  Cancellation is performed by the * {@code cancel} method.  Additional methods are provided to * determine if the task completed normally or was cancelled. Once a * computation has completed, the computation cannot be cancelled. * If you would like to use a {@code Future} for the sake * of cancellability but not provide a usable result, you can * declare types of the form {@code Future<?>} and * return {@code null} as a result of the underlying task. * * <p> * <b>Sample Usage</b> (Note that the following classes are all * made-up.) * <pre> {@code * interface ArchiveSearcher { String search(String target); } * class App { *   ExecutorService executor = ... *   ArchiveSearcher searcher = ... *   void showSearch(final String target) *       throws InterruptedException { *     Future<String> future *       = executor.submit(new Callable<String>() { *         public String call() { *             return searcher.search(target); *         }}); *     displayOtherThings(); // do other things while searching *     try { *       displayText(future.get()); // use future *     } catch (ExecutionException ex) { cleanup(); return; } *   } * }}</pre> * * The {@link FutureTask} class is an implementation of {@code Future} that * implements {@code Runnable}, and so may be executed by an {@code Executor}. * For example, the above construction with {@code submit} could be replaced by: *  <pre> {@code * FutureTask<String> future = *   new FutureTask<String>(new Callable<String>() { *     public String call() { *       return searcher.search(target); *   }}); * executor.execute(future);}</pre> * * <p>Memory consistency effects: Actions taken by the asynchronous computation * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a> * actions following the corresponding {@code Future.get()} in another thread. * * @see FutureTask * @see Executor * @since 1.5 * @author Doug Lea * @param <V> The result type returned by this Future's {@code get} method */public interface Future<V> {    /**     * Attempts to cancel execution of this task.  This attempt will     * fail if the task has already completed, has already been cancelled,     * or could not be cancelled for some other reason. If successful,     * and this task has not started when {@code cancel} is called,     * this task should never run.  If the task has already started,     * then the {@code mayInterruptIfRunning} parameter determines     * whether the thread executing this task should be interrupted in     * an attempt to stop the task.     *     * <p>After this method returns, subsequent calls to {@link #isDone} will     * always return {@code true}.  Subsequent calls to {@link #isCancelled}     * will always return {@code true} if this method returned {@code true}.     *     * @param mayInterruptIfRunning {@code true} if the thread executing this     * task should be interrupted; otherwise, in-progress tasks are allowed     * to complete     * @return {@code false} if the task could not be cancelled,     * typically because it has already completed normally;     * {@code true} otherwise     */    boolean cancel(boolean mayInterruptIfRunning);    /**     * Returns {@code true} if this task was cancelled before it completed     * normally.     *     * @return {@code true} if this task was cancelled before it completed     */    boolean isCancelled();    /**     * Returns {@code true} if this task completed.     *     * Completion may be due to normal termination, an exception, or     * cancellation -- in all of these cases, this method will return     * {@code true}.     *     * @return {@code true} if this task completed     */    boolean isDone();    /**     * Waits if necessary for the computation to complete, and then     * retrieves its result.     *     * @return the computed result     * @throws CancellationException if the computation was cancelled     * @throws ExecutionException if the computation threw an     * exception     * @throws InterruptedException if the current thread was interrupted     * while waiting     */    V get() throws InterruptedException, ExecutionException;    /**     * Waits if necessary for at most the given time for the computation     * to complete, and then retrieves its result, if available.     *     * @param timeout the maximum time to wait     * @param unit the time unit of the timeout argument     * @return the computed result     * @throws CancellationException if the computation was cancelled     * @throws ExecutionException if the computation threw an     * exception     * @throws InterruptedException if the current thread was interrupted     * while waiting     * @throws TimeoutException if the wait timed out     */    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}


我们在看下netty 的future  为我们提供了 一些额外的方法

package io.netty.util.concurrent;import java.util.concurrent.CancellationException;import java.util.concurrent.TimeUnit;/** * The result of an asynchronous operation. */@SuppressWarnings("ClassNameSameAsAncestorName")public interface Future<V> extends java.util.concurrent.Future<V> 

isSuccess 方法: 如果仅仅的是IO 操作完成返回ture

/**     * Returns {@code true} if and only if the I/O operation was completed     * successfully.     */    boolean isSuccess();

isCancellable 方法:可以通过cancel 方法取消 就返回true;

    /**     * returns {@code true} if and only if the operation can be cancelled via {@link #cancel(boolean)}.     */    boolean isCancellable();


addListener 方法:向future 添加指定listener实例,当futrue完成的话 ,listener实例将会被调用接收到结果,是一个观察者模式
  /**     * Adds the specified listener to this future.  The     * specified listener is notified when this future is     * {@linkplain #isDone() done}.  If this future is already     * completed, the specified listener is notified immediately.     */    Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);

可以添加多个listener

   /**     * Adds the specified listeners to this future.  The     * specified listeners are notified when this future is     * {@linkplain #isDone() done}.  If this future is already     * completed, the specified listeners are notified immediately.     */    Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);


sync() 等待计算的完成,如果失败则抛出失败的原因

   /**     * Waits for this future until it is done, and rethrows the cause of the failure if this future     * failed.     */    Future<V> sync() throws InterruptedException;


await() 
 /**     * Waits for this future to be completed.     *     * @throws InterruptedException     *         if the current thread was interrupted     */    Future<V> await() throws InterruptedException;


 
 /**     * Return the result without blocking. If the future is not done yet this will return {@code null}.     *     * As it is possible that a {@code null} value is used to mark the future as successful you also need to check     * if the future is really done with {@link #isDone()} and not relay on the returned {@code null} value.     */    V getNow();

为什么Netty 自己实现JDK future ?  我们并不清楚什么时候调用get()方法 而netty 帮我们实现了 addListeners 监听 程序是否执行完成 ,而且 提供了是否成功的方法,而jdk future 只能判断是否完成,不清楚是正常完成还是 异常结束。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来我们看下ChannelFuture 源码: 继承了Future

它表示异步的 channle IO操作的结果,Netty所有的IO 操作都是异步的,它就意味着IO的调用返回 可能方法正在执行,你将会得到一个ChannelFuture ,来反馈它的状态结果。

ChannelFuture  要么是uncompleted要么是completed,当一个IO操作开始事,一个新的future会被创建,新的future 既不是成功的也不是失败的也不是取消的,因为io操作是尚

未完成的,如果io操作完成了或者是成功了失败了,future就会被标记为完成了并且携带信息,比如失败的信息,请注意 失败和取消都属于完成的状态。

可以看下面 javadoc 里面的图形 来表示 io的状态。

使用addListener 来代替await(),addListener 是一个非阻塞的方法,并且在IO操作完成时收到通知,并且可以做任何后续的任务处理,await是个阻塞的方法。

不要在ChannelHandler中调用 await()方法ChannelHandler当中事件处理器方法(就是客户建立连接,或者读取等)通过会被IO线程调用,如果await被事件处理器调用,

而事件处理器又被IO线程调用的,那么所等待的IO操作就可能永远不会完成,因为await方法可能会阻塞所等待的IO操作 就是死锁。

不要将 IO 超时 与 wait 超时 混淆  : await(long timeout, TimeUnit unit) awaitUninterruptibly(long timeoutMillis); awaitUninterruptibly(long timeout, TimeUnit unit); 与IO超时是没

有任何关系的是等待超时,如果一个IO 超时,这个Future将会被标记失败


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. * <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. * <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. * <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. * * <h3>Prefer {@link #addListener(GenericFutureListener)} to {@link #await()}</h3> * * 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. * <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. * <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. * * <h3>Do not call {@link #await()} inside {@link ChannelHandler}</h3> * <p> * 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. * <pre> * // BAD - NEVER DO THIS * {@code @Override} * public void channelRead({@link ChannelHandlerContext} ctx, Object msg) { *     {@link ChannelFuture} future = ctx.channel().close(); *     future.awaitUninterruptibly(); *     // 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. * * <h3>Do not confuse I/O timeout and await timeout</h3> * * 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: * <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. *     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> * {@link 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 * } * </pre> */public interface ChannelFuture extends Future<Void>


我们看看 ChannelFuture 里面的方法

channel() 方法:返回一个与future 发生相关IO 操作的 channel通道  ,其他的方法 与父类方法相似 就不介绍了

  /**     * Returns a channel where the I/O operation associated with this     * future takes place.     */    Channel channel();


我们在回到initAndRegister 

final ChannelFuture initAndRegister() {        Channel channel = null;        try {            channel = channelFactory.newChannel();            init(channel);        } catch (Throwable t) {            if (channel != null) {                // channel can be null if newChannel crashed (eg SocketException("too many open files"))                channel.unsafe().closeForcibly();            }            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);        }        ChannelFuture regFuture = config().group().register(channel);        if (regFuture.cause() != null) {            if (channel.isRegistered()) {                channel.close();            } else {                channel.unsafe().closeForcibly();            }        }        // If we are here and the promise is not failed, it's one of the following cases:        // 1) If we attempted registration from the event loop, the registration has been completed at this point.        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.        // 2) If we attempted registration from the other thread, the registration request has been successfully        //    added to the event loop's task queue for later execution.        //    i.e. It's safe to attempt bind() or connect() now:        //         because bind() or connect() will be executed *after* the scheduled registration task is executed        //         because register(), bind(), and connect() are all bound to the same thread.        return regFuture;    }


我们继续看init()方法 :

总结就是 进行一些属性赋值,然后生成通道的管道,此方法极为重要

   @Override    void init(Channel channel) throws Exception {        final Map<ChannelOption<?>, Object> options = options0();        synchronized (options) {            setChannelOptions(channel, options, logger);        }        final Map<AttributeKey<?>, Object> attrs = attrs0();        synchronized (attrs) {            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {                @SuppressWarnings("unchecked")                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();                channel.attr(key).set(e.getValue());            }        }        ChannelPipeline p = channel.pipeline();        final EventLoopGroup currentChildGroup = childGroup;        final ChannelHandler currentChildHandler = childHandler;        final Entry<ChannelOption<?>, Object>[] currentChildOptions;        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;        synchronized (childOptions) {            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));        }        synchronized (childAttrs) {            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));        }        p.addLast(new ChannelInitializer<Channel>() {            @Override            public void initChannel(final Channel ch) throws Exception {                final ChannelPipeline pipeline = ch.pipeline();                ChannelHandler handler = config.handler();                if (handler != null) {                    pipeline.addLast(handler);                }                ch.eventLoop().execute(new Runnable() {                    @Override                    public void run() {                        pipeline.addLast(new ServerBootstrapAcceptor(                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));                    }                });            }        });    }


原创粉丝点击