啓動服務端的代碼以下:java
public static void main(String[] args) throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new SecureChatServerInitializer(sslCtx)); b.bind(PORT).sync().channel().closeFuture().sync();//調用bind方法時啓動線程 } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
其中ServerBootstrap的bind方法經過調用父類的initAndRegister構造一個Channel,並把Channel註冊到EventLoopGrouppromise
final ChannelFuture initAndRegister() { final Channel channel = channelFactory().newChannel(); try { init(channel); } catch (Throwable t) { channel.unsafe().closeForcibly(); return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t); } ChannelFuture regFuture = group().register(channel);//把channel註冊到EventLoopGroup(NioEventLoopGroup) if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } } return regFuture; }
註冊代碼以下ide
public ChannelFuture register(Channel channel) { return next().register(channel);//從EventLoopGroup裏獲取一個NioEventLoop,並註冊channel } public ChannelFuture register(final Channel channel, final ChannelPromise promise) { if (channel == null) { throw new NullPointerException("channel"); } if (promise == null) { throw new NullPointerException("promise"); } channel.unsafe().register(this, promise);//註冊過程實際上是把NioEventLoop(中的selector)註冊到channel return promise; }
能夠看到Channel註冊到EventLoopGroup的過程被轉換成NioEventLoop(中的selector)註冊到channel。oop
轉換後的註冊過程以下:this
public final void register(EventLoop eventLoop, final ChannelPromise promise) { if (eventLoop == null) { throw new NullPointerException("eventLoop"); } if (isRegistered()) { promise.setFailure(new IllegalStateException("registered to an event loop already")); return; } if (!isCompatible(eventLoop)) { promise.setFailure( new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName())); return; } AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else {//啓動的時候執行else中的代碼 try { eventLoop.execute(new OneTimeTask() {//這個方法調用回啓動 @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); } } }
啓動的時候執行else裏的代碼,調用eventLoop的execute方法,該方法啓動eventLoop中的線程,並把register0做爲一個任務,提交給eventLoop中的任務隊列。線程
register0的代碼以下:code
private void register0(ChannelPromise promise) { try { if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } boolean firstRegistration = neverRegistered; doRegister(); neverRegistered = false; registered = true; safeSetSuccess(promise); pipeline.fireChannelRegistered(); if (firstRegistration && isActive()) { pipeline.fireChannelActive(); } } catch (Throwable t) { closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); } } protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().selector, 0, this);//SelectableChannel return; } catch (CancelledKeyException e) { if (!selected) { eventLoop().selectNow(); selected = true; } else { throw e; } } } }
能夠看到最終的註冊過程是調用jdk的SelectableChannel的register方法,把NioEventLoop的selector註冊到了channel上。對象
下面再看看eventLoop中的線程執行循環,代碼以下:隊列
@Override protected void run() { for (;;) { boolean oldWakenUp = wakenUp.getAndSet(false); try { if (hasTasks()) { selectNow(); } else { select(oldWakenUp); if (wakenUp.get()) { selector.wakeup(); } } cancelledKeys = 0; needsToSelectAgain = false; final int ioRatio = this.ioRatio; if (ioRatio == 100) { processSelectedKeys(); runAllTasks(); //執行任務隊列中的任務 } else { final long ioStartTime = System.nanoTime(); processSelectedKeys(); final long ioTime = System.nanoTime() - ioStartTime; runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//執行任務隊列中的任務 } if (isShuttingDown()) { closeAll(); if (confirmShutdown()) { break; } } } catch (Throwable t) { try { Thread.sleep(1000); } catch (InterruptedException e) { } } } }
總結一下:ip
調用ServerBootstrap的bind方法啓動服務端的時候,先建立一個Channel對象,而後從NioEventLoopGroup裏獲取一個NioEventLoop,而後啓動NioEventLoop中的線程,同時把註冊任務提交到NioEventLoop中的任務隊列。NioEventLoop中的線程循環執行,在必定條件下會執行任務隊列中的註冊任務,從而把NioEventLoop的selector註冊到Channel對象。