在https://blog.csdn.net/ybin__/article/details/81007018 中分析了NioEventGroupLoop的初始化,這一章主要ServerBootstrap的初始化,及Netty服務端的啓動。
- ServerBootstrap的UML圖:
ServerBootstrap繼承至AbstractBootstrap,ServerBootStrap初始化的時候會將服務端的NioEventGroupLoop,handler及配置保存至AbstractBootStrap中,將客戶端的配置保存至ServerBootStrap中。
服務端初始化代碼:
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup) //1.保存NioEventLoopGroup
.channel(NioServerSocketChannel.class) //2.設置服務端channel
.option(ChannelOption.SO_BACKLOG, 128) //3.保存服務端配置
.childOption(ChannelOption.SO_KEEPALIVE, true) //4.保存客戶端配置
.handler(new ChannelHandler()) //5.保存服務端handler
.childHandler(new ChannelInitializer<SocketChannel>() { //6.保存客戶端handler
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundA(), new ChannelInboundB(), new ChannelInboundC());
}
});
try {
ChannelFuture future = b.bind(port).sync(); //7.啓動服務端
log.info("server start running");
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
1).保存NioEventGroupLoop,如下,可以看到ServerBootstrap將處理客戶端的childGroup保存至ServerBootStrap,將parentGroup保存至父類AbstractBootStrap中;
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;
}
2).保存服務端NioServerSocketChannel,在綁定端口的時候會反射調用它的構造方法,爲服務端創建一個channel。
//1.調用無參構造
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
//2.獲取初始化完成的SelectorProvider
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
//3.通過JDK的SelectorProvider打開一個ServerSocketChannel用於監聽事件
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
//4.調用父類構造方法,並保存config;
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
//5.調用父類構造方法,並保存服務端需要監聽的事件類型(SelectionKey.OP_ACCEPT),設置非阻塞
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);
}
}
//6.保存服務端channel,爲該channel設置id,並實例化unsafe,後續獲取內存等使用,並初始化pipeline,
//pipeline是netty的調用鏈路,本身的數據結構是一個鏈表,採用責任鏈模式處理事件的傳播等;
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
服務端啓動:
- 在配置好ServerbootStrap後,調用bind(int prot)方法綁定監聽端口,監聽事件;
- 最後調用到io.netty.bootstrap.AbstractBootstrap#doBind方法,在該方法中,netty會初始化並註冊channel到selector的多路複用器上,及上述NioServerSocketChannel初始化的過程。
channel初始化完成後,調用io.netty.bootstrap.ServerBootstrap#init方法,爲channel設置上創建ServerBootStrap時配置的參數,如IO參數,channelHandler事件處理器等。
最後調用獲取配置的boosGroup,並調用其register(channel)方法,即io.netty.channel.MultithreadEventLoopGroup#register。在該方法中,會先調用io.netty.channel.MultithreadEventLoopGroup#next獲取可能空閒的NioEventLoop,這裏實現方式實際就是上一章說到的netty使用策略模式爲NioEventLoop提供了兩種選擇策略,如果NioEventLoopGroup的線程數是2的冪次方則是每次取自增前的數 & 線程池長度-1,如果不是就是自增前的數 對 線程池長度取餘。
獲取到可能空閒的NioEventLoop後最終調用到io.netty.channel.nio.AbstractNioChannel#doRegister,將NioEventLoop的selector註冊到javaChannel上,並返回需要關注的事件。final ChannelFuture initAndRegister() { Channel channel = null; try { channel = channelFactory.newChannel(); init(channel); } catch (Throwable t) { if (channel != null) { channel.unsafe().closeForcibly(); } 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(); } } return regFuture; }
@Override protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { selectionKey = javaChannel().register(eventLoop().selector, 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; } } } }
- 最後調用到io.netty.bootstrap.AbstractBootstrap#doBind0,獲取該channel的NioEventLoop,提交一個綁定端口的任務,執行io.netty.util.concurrent.SingleThreadEventExecutor#execute中的execute方法;
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { 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()); } } }); }
-
在io.netty.util.concurrent.SingleThreadEventExecutor#execute中會首先判斷當前線程是否是選擇的NioEvenLoop中保存執行線程,不是的話則會調用io.netty.util.concurrent.SingleThreadEventExecutor#startThread方法,如果該線程是第一次start則會啓動事件輪詢處理線程(下章分析),並將綁定端口的任務加到NioEventLoop的任務執行隊列中,交由事件輪詢處理任務處理,這裏就是Nettyr異步串行處理任務的體現,將不是同一個NioEventLoop線程的任務新起一個線程執行,而同線程的任務,則放到該NioEventLoop的隊列中,等NioEventLoop的線程來處理,避免了多線的競爭,不同線程的任務啓動新線程異步執行。
到這裏netty服務端已啓動,下面就開始監聽事件,處理事件了,下一章再繼續分析。