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();
/** * 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;
/** * 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)); } }); } }); }
- Netty 源码分析(三)
- netty(十一)源码分析之ByteBuf 三
- netty源码分析 之三 transport(channel)
- Netty源码分析(三)—数据管道ChannelPipeline源码分析
- Netty 源码分析(一)
- Netty 源码分析(二)
- 自顶向下深入分析Netty(三)--Bootstrap源码分析
- netty源码分析(三)Netty服务端ServerBootstrap的初始化与反射在其中的应用分析
- 【Netty4.x】Netty源码分析(三)之LineBasedFrameDecoder
- Netty源码分析之三【我就是大名鼎鼎的 EventLoop】
- Netty 4.0 源码分析(四):ByteBuf
- netty(十)源码分析之ByteBuf
- netty(十三)源码分析之Channel
- netty(十四)源码分析之Unsafe
- netty(十五)源码分析之ChannnelPipeline
- Netty 源码分析
- netty源码分析小结
- netty 源码分析一
- 总结-2017/10/19
- 【安全牛学习笔记】HTTP协议
- vijos1320 清点人数 题解
- 【宏观】从三期到无限期动态模型
- Wamp(windows)配置虚拟主机
- Netty 源码分析(三)
- 【Java集合框架源码分析(JDK1.7)】-LinkedList源码分析
- Arduino学习笔记——LED跑马灯
- 【网络编程】socket通信流程图(tcp)
- 引用作为函数参数以及返回值的好处
- IntelliJ IDEA 15 创建maven项目
- JSP九大内置对象及四个作用域
- Deep Learning:深度前馈神经网络(四)
- Android Studio 提高开发效率的非常有用的插件(一)