1. 服務端啓動步驟
一個 Netty 服務端配置啓動代碼如下,其大略的流程爲以下幾個步驟:
- 創建 NioEventLoopGroup 實例,這個類是 Netty 的 Reactor 線程池實現之一,其實際爲 EventLoop 的容器,而 EventLoop 的主要職責是處理所有註冊到本線程多路複用器 Selector 上的 Channel。代碼中創建了兩個 NioEventLoopGroup 實例,這是 主從 Reactor 多線程模式 的體現
- 創建 ServerBootstrap 實例,該類是 Netty 服務端的啓動輔助類,提供一系列方法用於設置服務端啓動相關的參數
- ServerBootstrap 對象調用 group() 方法設置並綁定主從 Reactor 線程池
- ServerBootstrap 對象調用 channel() 方法設置服務端 Channel 實例類型
- ServerBootstrap 對象調用 handler() 方法設置服務端 MainReactor 線程池業務處理器
- ServerBootstrap 對象調用帶 child 前綴的方法設置服務端 SubReactor 線程池配置,比如 childHandler() 方法設置SubReactor 線程池業務處理器
- ServerBootstrap 對象調用 bind() 方法綁定並啓動監聽端口
public class NettyServer {
private static final int DEFAULT_PORT = 10086;
public static void start() {
new Thread(() -> {
// 創建監聽線程組, 監聽客戶端請求
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
// 創建工作線程組, 處理請求
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
// 服務器輔助啓動類配置
new ServerBootstrap().group(bossGroup, workerGroup)
// 設置 channel 類型爲NIO類型
.channel(NioServerSocketChannel.class)
// 設置連接配置參數
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler())
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
// 配置入站、出站事件handler
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
// 配置入站、出站事件channel
ch.pipeline()
.addLast(new LineBasedFrameDecoder(1024))
.addLast(new StringDecoder(CharsetUtil.UTF_8))
.addLast(new StringEncoder(CharsetUtil.UTF_8))
.addLast(new ServerHandler());
}
})
// 綁定端口
.bind(DEFAULT_PORT)
.addListener(future -> {
if (future.isSuccess()) {
System.out.println(new Date() + ": 端口[" + DEFAULT_PORT + "]綁定成功!");
} else {
System.err.println("端口[" + DEFAULT_PORT + "]綁定失敗!");
}
});
}, "Server").start();
}
}
2. 服務端啓動流程詳解
服務端啓動流程圖如下,各組件間的關係可參考 Netty源碼分析(1)-核心組件與架構,其主要流程可以分爲以下幾個部分:
- 事件分發組件的配置及初始化
- 業務處理組件 Channel 的初始化及註冊,註冊過程即事件循環線程的啓動過程
- 綁定服務端 ServerSocketChannel 到指定的端口
2.1 事件分發組件配置及初始化
這部分對應以上流程圖步驟 1-5,其中比較關鍵的流程如下:
-
new NioEventLoopGroup()
創建 Reactor 線程池實例,其關鍵邏輯在父類MultithreadEventExecutorGroup
的構造方法中,重要步驟如下:new ThreadPerTaskExecutor(new DefaultThreadFactory())
生成 Executor 實例,並指定其線程工廠- 調用
newChild()
方法爲當前 group 新建 NioEventLoop 實例,並指定其 Executor 入參爲 ThreadPerTaskExecutor 對象,該對象後續將用於創建和啓動 EventLoop 線程 - 如果有一個 NioEventLoop 實例新建失敗,調用已創建的每個 NioEventLoop 實例的 shutdownGracefully() 方法啓動事件循環線程
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { if (nThreads <= 0) { throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads)); } if (executor == null) { // #1 executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { boolean success = false; try { // #2 children[i] = newChild(executor, args); success = true; } catch (Exception e) { // TODO: Think about if this is a good exception type throw new IllegalStateException("failed to create a child event loop", e); } finally { if (!success) { for (int j = 0; j < i; j ++) { // #3 children[j].shutdownGracefully(); } for (int j = 0; j < i; j ++) { EventExecutor e = children[j]; try { while (!e.isTerminated()) { e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } } catch (InterruptedException interrupted) { // Let the caller handle the interruption. Thread.currentThread().interrupt(); break; } } } } } chooser = chooserFactory.newChooser(children); final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } }; for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener); } Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }
-
new ServerBootstrap().group(bossGroup, workerGroup)
新建 ServerBootstrap 對象,並調用其group()
方法配置好主從 Reactor 線程池。其中 MainReactor 線程池將會保存在AbstractBootstrap.group 變量
,SubReactor 線程池以ServerBootstrap.childGroup 變量
保存public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { super.group(parentGroup); if (childGroup == null) { throw new NullPointerException("childGroup"); } if (this.childGroup != null) { throw new IllegalStateException("childGroup set already"); } this.childGroup = childGroup; return this; }
-
ServerBootstrap#channel()
方法實際配置了一個ReflectiveChannelFactory
工廠類,用於創建指定的 NioServerSocketChannel 對象public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); } public B channelFactory(ChannelFactory<? extends C> channelFactory) { if (channelFactory == null) { throw new NullPointerException("channelFactory"); } if (this.channelFactory != null) { throw new IllegalStateException("channelFactory set already"); } this.channelFactory = channelFactory; return self(); }
-
以
ServerBootstrap#childHandler()
方法爲例,帶 child 前綴的方法都用來配置 SubReactor 線程池所需的處理器等,這些配置在 MainReactor 將連接註冊到 SubReactor 上之後 IO 讀寫會用到public ServerBootstrap childHandler(ChannelHandler childHandler) { if (childHandler == null) { throw new NullPointerException("childHandler"); } this.childHandler = childHandler; return this; }
2.2 業務處理組件 Channel 的初始化及註冊
-
ServerBootstrap#bind()
方法實際調用到AbstractBootstrap#doBind()
方法,這個方法是整個服務端啓動的入口,主要分爲了以下 2 個部分,本節主要分析initAndRegister()
流程initAndRegister()
初始化 Channel,並將其註冊到 Selector 上doBind0()
將 Channel 綁定監聽指定端口
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; } }
-
AbstractBootstrap#initAndRegister()
方法的作用見名知意,其作用就是初始化 channel 並完成註冊,其中關鍵的部分如下:- 首先通過配置的Channel 工廠類創建指定 Channel 對象,然後通過 init(channel) 方法初始化 Channel
- config().group().register(channel) 將初始化完畢的 Channel 註冊到 MainReactor 中某個事件循環線程 Selector 上
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); } // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t); } ChannelFuture regFuture = config().group().register(channel); if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } } return regFuture; }
-
channelFactory.newChannel()
通過反射調用了NioServerSocketChannel
無參構造方法,可知其newSocket()
方法實際是返回了一個 JDK 中的ServerSocketChannel
對象,NioServerSocketChannel
實際就是這個對象的進一步封裝。在NioServerSocketChannel
的有參構造中可以看到,其調用了父類構造方法,並指定了其監聽的就緒事件爲SelectionKey.OP_ACCEPT
。此處往上追溯到其AbstractNioChannel
父類構造方法,可以看到 JDK 中 NIO 設置非阻塞的標準操作,並保留了監聽的就緒事件的標識readInterestOp
,這個標識會在之後開始讀取 IO 數據的時候用於設置監聽事件public NioServerSocketChannel() { this(newSocket(DEFAULT_SELECTOR_PROVIDER)); } public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); } // AbstractNioChannel 構造方法 protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent); this.ch = ch; this.readInterestOp = readInterestOp; try { ch.configureBlocking(false); } catch (IOException e) { try { ch.close(); } catch (IOException e2) { if (logger.isWarnEnabled()) { logger.warn( "Failed to close a partially initialized socket.", e2); } } throw new ChannelException("Failed to enter non-blocking mode.", e); } }
-
追溯
NioServerSocketChannel
父類構造方法最終來到AbstractChannel
,可以到其主要有兩個動作:- newUnsafe() 新建 NioMessageUnsafe 對象,該類負責實際的 IO 讀寫動作
- newChannelPipeline() 新建 DefaultChannelPipeline 對象
protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }
-
DefaultChannelPipeline
構造方法中會新建TailContext
和HeadContext
對象,並將其前後指針互相指向對象,形成雙向處理鏈表protected DefaultChannelPipeline(Channel channel) { this.channel = ObjectUtil.checkNotNull(channel, "channel"); succeededFuture = new SucceededChannelFuture(channel, null); voidPromise = new VoidChannelPromise(channel, true); tail = new TailContext(this); head = new HeadContext(this); head.next = tail; tail.prev = head; }
-
Channel 對象的創建告一段落,回到初始化 Channel 的方法
AbstractBootstrap#init()
。這是個抽象方法,其實現爲ServerBootstrap#init()
。這個方法中最重要的邏輯就是代碼p.addLast()
調用的部分,這部分ChannelInitializer
對象被添加到流處理鏈,會在之後的流程中將ServerBootstrapAcceptor
註冊到 MainReactor 的處理鏈中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(0)); } synchronized (childAttrs) { currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0)); } 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)); } }); } }); }
-
進入
DefaultChannelPipeline#addLast()
方法,可以看到其內部主要做了 3 件事:- newContext() 將 ChannelHandler 封裝到 DefaultChannelHandlerContext 對象
- addLast0() 將新建的 ChannelHandlerContext 對象加入到雙向鏈表中
- 此時 Channel 還沒有完成註冊,callHandlerCallbackLater() 方法將新建一個 PendingHandlerAddedTask 對象,用於註冊完成後執行 callHandlerAdded0() 方法回調處理器實現的 handlerAdd() 方法
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { checkMultiplicity(handler); newCtx = newContext(group, filterName(name, handler), handler); addLast0(newCtx); // If the registered is false it means that the channel was not registered on an eventloop yet. // In this case we add the context to the pipeline and add a task that will call // ChannelHandler.handlerAdded(...) once the channel is registered. if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { newCtx.setAddPending(); executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } callHandlerAdded0(newCtx); return this; }
-
Channel 的初始化結束,接下來就是註冊 Channel。
config().group().register(channel)
實際調用到了NioEventLoopGroup
超類MultithreadEventLoopGroup#register()
方法,最終其實是選中事件循環線程池中的一個NioEventLoop
事件循環對象完成註冊,實現是調用其超類SingleThreadEventLoop#register()
方法@Override public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }
-
從代碼可以看到,註冊是由
Unsafe
類完成的,其實現爲AbstractUnsafe#register()
。此時事件循環線程還沒有啓動,故其會將AbstractUnsafe#register0()
方法包裝成異步任務扔到事件循環對象中執行@Override public final void register(EventLoop eventLoop, final ChannelPromise promise) { ...... AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { register0(promise); } }); } catch (Throwable t) { logger.warn( "Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, t); closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); } } }
-
以上任務其實提交到了
SingleThreadEventExecutor#execute()
,這個方法比較關鍵的其實只有兩步:- addTask() 將異步任務添加到任務隊列中
- 此時事件循環尚未啓動,調用 startThread() 新建線程並啓動
@Override public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } boolean inEventLoop = inEventLoop(); addTask(task); if (!inEventLoop) { startThread(); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); } }
-
啓動線程調用到
SingleThreadEventExecutor#doStartThread()
,此處代碼executor.execute()
實際是通過ThreadPerTaskExecutor
新建並啓動線程,至此則SingleThreadEventExecutor.this.run()
方法被調用private void doStartThread() { assert thread == null; executor.execute(new Runnable() { @Override public void run() { thread = Thread.currentThread(); if (interrupted) { thread.interrupt(); } boolean success = false; updateLastExecutionTime(); try { SingleThreadEventExecutor.this.run(); success = true; } catch (Throwable t) { logger.warn("Unexpected exception from an event executor: ", t); } finally { ...... } } }); }
-
SingleThreadEventExecutor.this.run()
爲抽象方法,其實現爲NioEventLoop#run()
,其主要邏輯如下:for 空循環正式啓動事件循環線程。循環中select() 方法通過 Selector 輪詢 IO 就緒事件,之後根據 ioRatio 配置分配processSelectedKeys()處理 IO 事件 和 runAllTasks() 處理其他任務的時間
protected void run() { for (;;) { try { switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { case SelectStrategy.CONTINUE: continue; case SelectStrategy.BUSY_WAIT: // fall-through to SELECT since the busy-wait is not supported with NIO case SelectStrategy.SELECT: select(wakenUp.getAndSet(false)); if (wakenUp.get()) { selector.wakeup(); } // fall through default: } cancelledKeys = 0; needsToSelectAgain = false; final int ioRatio = this.ioRatio; if (ioRatio == 100) { try { processSelectedKeys(); } finally { // Ensure we always run tasks. runAllTasks(); } } else { final long ioStartTime = System.nanoTime(); try { processSelectedKeys(); } finally { // Ensure we always run tasks. final long ioTime = System.nanoTime() - ioStartTime; runAllTasks(ioTime * (100 - ioRatio) / ioRatio); } } } catch (Throwable t) { handleLoopException(t); } // Always handle shutdown even if the loop processing threw an exception. try { if (isShuttingDown()) { closeAll(); if (confirmShutdown()) { return; } } } catch (Throwable t) { handleLoopException(t); } } }
-
SingleThreadEventExecutor#runAllTasks()
會將隊列中的任務處理掉,則之前的異步任務AbstractUnsafe#register0()
被執行,其處理步驟如下:- doRegister() 完成 ServerSocketChannel 的註冊
- pipeline.invokeHandlerAddedIfNeeded() 回調 Handler 處理器的 handlerAdded() 方法
- pipeline.fireChannelRegistered() 通知 Channel 註冊事件,由處理器做相應處理
- 此時 ServerSocketChannel 如果是激活狀態,且是第一次註冊,則 pipeline.fireChannelActive() 通知 Channel 激活事件,由處理器做對應處理
private void register0(ChannelPromise promise) { try { // check if the channel is still open as it could be closed in the mean time when the register // call was outside of the eventLoop if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } boolean firstRegistration = neverRegistered; doRegister(); neverRegistered = false; registered = true; // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the // user may already fire events through the pipeline in the ChannelFutureListener. pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise); pipeline.fireChannelRegistered(); // Only fire a channelActive if the channel has never been registered. This prevents firing // multiple channel actives if the channel is deregistered and re-registered. if (isActive()) { if (firstRegistration) { pipeline.fireChannelActive(); } else if (config().isAutoRead()) { // This channel was registered before and autoRead() is set. This means we need to begin read // again so that we process inbound data. // // See https://github.com/netty/netty/issues/4805 beginRead(); } } } catch (Throwable t) { // Close the channel directly to avoid FD leak. closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); } }
-
AbstractNioChannel#doRegister()
中實際調用了ServerSocketChannel#register()
實現將 Channel 註冊到 Selector 上,這屬於Java 中 NIO 的標準操作。需注意此時 Channel 的監聽標識爲 0,也就是說此時 ServerSocketChannel 僅僅註冊成功了,還不能監聽任何網絡操作。不過之後可以通過SelectionKey#interestOps()
方法修改監聽操作位爲指定值,也就是步驟 3 提到的標識protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) { if (!selected) { // Force the Selector to select now as the "canceled" SelectionKey may still be // cached and not removed because no Select.select(..) operation was called yet. eventLoop().selectNow(); selected = true; } else { // We forced a select operation on the selector before but the SelectionKey is still cached // for whatever reason. JDK bug ? throw e; } } } }
-
pipeline.invokeHandlerAddedIfNeeded()
最終調用到DefaultChannelPipeline#callHandlerAddedForAllHandlers()
,則步驟 7 中封裝的PendingHandlerAddedTask#execute
執行,可以看到這個任務的核心爲DefaultChannelPipeline#callHandlerAdded0()
@Override void execute() { EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) { callHandlerAdded0(ctx); } else { try { executor.execute(this); } catch (RejectedExecutionException e) { if (logger.isWarnEnabled()) { logger.warn( "Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.", executor, ctx.name(), e); } remove0(ctx); ctx.setRemoved(); } } }
-
DefaultChannelPipeline#callHandlerAdded0()
會依次回調處理器的handlerAdded()
方法,則步驟6 的ChannelInitializer#handlerAdded()
會被調用,最終調用ChannelInitializer#initChannel()
將ServerBootstrapAcceptor
註冊到 MainReactor 的處理鏈中,至此再執行完 步驟13 剩下的步驟則註冊流程完成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)); } }); } });
2.3 服務端 ServerSocketChannel 綁定
-
Channel 初始化並註冊完成後,就進入了綁定流程,也就是方法
AbstractBootstrap#doBind0()
的調用。可以看到方法內部的主體是向已經註冊的 Channel 所屬的事件循環線程提交一個異步任務,該任務主要調用到AbstractChannel#bind()
方法private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }
-
AbstractChannel#bind()
方法內部調用到DefaultChannelPipeline#bind()
方法,這個方法邏輯很簡單,只是使用TailContext
的引用調到了AbstractChannelHandlerContext#bind()
方法public final ChannelFuture bind(SocketAddress localAddress) { return tail.bind(localAddress); }
-
AbstractChannelHandlerContext#bind()
內部邏輯也不復雜,首先通過findContextOutbound()
方法從流處理雙向鏈表尾部往前找到一個處理出站事件的處理器,也就是HeadContext
,之後調用其invokeBind()
方法public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) { if (localAddress == null) { throw new NullPointerException("localAddress"); } if (isNotValidPromise(promise, false)) { // cancelled return promise; } final AbstractChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeBind(localAddress, promise); } else { safeExecute(executor, new Runnable() { @Override public void run() { next.invokeBind(localAddress, promise); } }, promise, null); } return promise; }
-
HeadContext#invokeBind()
方法其實是藉助了Unsafe
類進行綁定操作的,最終也就調到了AbstractUnsafe#bind()
方法。這個方法主要完成了兩件事:- doBind() 完成 ServerSocketChannel 綁定到指定端口
- 提交異步任務,調用 pipeline.fireChannelActive() 通知 Channel 已經激活,從而回調處理器中的 channelActive() 方法
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) { assertEventLoop(); if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } // See: https://github.com/netty/netty/issues/576 if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) { // Warn a user about the fact that a non-root user can't receive a // broadcast packet on *nix if the socket is bound on non-wildcard address. logger.warn( "A non-root user can't receive a broadcast packet if the socket " + "is not bound to a wildcard address; binding to a non-wildcard " + "address (" + localAddress + ") anyway as requested."); } boolean wasActive = isActive(); try { doBind(localAddress); } catch (Throwable t) { safeSetFailure(promise, t); closeIfClosed(); return; } if (!wasActive && isActive()) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireChannelActive(); } }); } safeSetSuccess(promise); }
-
dobind()
爲抽象方法,其實現爲NioServerSocketChannel#doBind()
,可以看到內部邏輯其實就是將 ServerSocketChannel 綁定到指定端口進行監聽而已,屬於 Java 中 NIO 的標準操作,至此綁定流程結束protected void doBind(SocketAddress localAddress) throws Exception { if (PlatformDependent.javaVersion() >= 7) { javaChannel().bind(localAddress, config.getBacklog()); } else { javaChannel().socket().bind(localAddress, config.getBacklog()); } }