Netty服務端啟動過程相關原始碼分析
1、Netty 是怎麼建立服務端Channel的呢?
我們在使用ServerBootstrap.bind(埠)方法時,最終呼叫其父類AbstractBootstrap中的doBind方法,相關原始碼如下:
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化和註冊
final ChannelFuture regFuture = initAndRegister();
.....
我們繼續跟進initAndRegister()這個方法,發現是使用channelFactory.newChannel() 完成channel的建立:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {......}
ChannelFactory在實現類ReflectiveChannelFactory中的實現細節,內部使用了反射的方式建立Channel:
public T newChannel() { try { return clazz.newInstance(); } catch (Throwable t) {......} }
這裡的ChannelFactory是通過 bootstrap.channel(NioServerSocketChannel.class) 加入的:
public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); }
- 綜上所述,Netty就是呼叫jdk底層方法建立NIO的channel,也就是通過反射完成NIO的channel建立。最後其包裝成Netty自己的Channel.
2、初始化服務端Channel是怎麼樣的執行流程?
在建立了Channel之後就呼叫AbstractBootstrap的init(channel)抽象方法完成初始化:
abstract void init(Channel channel) throws Exception;
伺服器端ServerBootstrap的init方法從原始碼來看主要完成工作為:
- 配置相關的Options和Attribute。
- 通過ChannelPipeline 新增相關邏輯處理器 ChannelHandler。
最後這些屬性會傳入ServerBootstrapAcceptor聯結器,通過ServerBootstrapAcceptor聯結器完成相應的初始化。
// We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
// In this case the initChannel(...) method will only be called after this method returns. Because
// of this we need to ensure we add our handler in a delayed fashion so all the users handler are
// placed in front of the ServerBootstrapAcceptor.
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
3、怎麼註冊selector?
在Java NIO 中註冊通道Channel到多路複用器Selector,並說明關注點SelectionKey.OP_ACCEPT,監聽ACCEPT事件通常我們會這樣寫:
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
Netty在底層將Channel註冊到事件輪詢器selector上就是基於此方法:
- 首先在初始化Channel後執行:
ChannelFuture regFuture = config().group().register(channel);
上面的程式碼實際是呼叫AbstractChannel的register方法,完成eventLoop的繫結。內部方法register0()中會呼叫AbstractNioChannel的doRegister() 方法:
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {......}
}
}
這裡其實就是呼叫NIO SelectableChannel的register方法。
- 從原始碼可以知道,註冊成功後這裡會以此執行伺服器handler中的回撥方法:handlerAdded ,channelActive
4、埠怎麼繫結呢?
一切OK之後就會呼叫AbstractChannel中的 bind 方法,這個方法又會呼叫NioServerSocketChannel 的 doBind 方法,從doBind方法可知是呼叫的原生NIO 的bind做繫結:
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
繫結完成後會執行程式碼:
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
這裡會呼叫DefaultChannelPipeline中的內部類HeadContext的channelActive方法進行事件傳播:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
readIfIsAutoRead();
}
那麼服務端到底是在哪裡 accept 連線的呢?
通過上面的程式碼我們跟進 AbstractChannel 的beginRead() 方法,繼而找到 AbstractNioChannel 的 doBeginRead() 方法:
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
上面的程式碼就是NIO程式設計中常用的寫法,這裡監聽ACCEPT事件就是NioServerSocketChannel建構函式呼叫父類傳入的SelectionKey.OP_ACCEPT:
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}