1. 主線
our thread就是我們自己寫的netty服務器的主線程的代碼
2. 啓動
在下面三處打上斷點
2.1 selector初始化是在NioEventLoopGroup初始化
運行程序:發現,查看NioEventLoopGroup
通過創建一個子的NioEventLoopGroup
,然後子的NioEventLoopGroup
又NioEventLoop
,並完成selector的創建
2.2 進入bind方法
跟進源碼:
private ChannelFuture doBind(final SocketAddress localAddress) {
// channel註冊、初始化、綁定到EventLoopGroup上
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
// 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);
// 如果沒有完成註冊操作會將註冊任務封裝成一個task,加入到regFuture的Listener上
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;
}
}
2.2.1 initAndRegister方法的具體實現
2.2.1.1initAndRegister中的init()方法
@Override
void init(Channel channel) {
// 初始化option
setChannelOptions(channel, newOptionsArray(), logger);
// 初始化屬性
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
// 構建一個pipeline
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;// 全局變量childGroup改名爲currentChildGroup
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
// ChannelInitializer完成將我們需要的handler加入到pipeline中
// 當將所有的handler加入到pipeline完成以後,ChannelInitializer結束,從pipeline中移除
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// new ServerBootstrapAcceptor()對象時將currentChildGroup作爲參數傳入
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
2.2.1.2 initAndRegister方法中的register()
因爲next方法我們之前已經講過了兩種選擇器
跟進register()的代碼
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "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;
// 判斷當前線程是否是NioEvenetLoop中的線程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
// 如果當前線程不在NioEvenetLoop中,將register0任務丟到eventLoop去執行
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);
}
}
}
繼續跟進register0
方法
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 使用java的jdk的Nio變成,將channel註冊到selector上
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;
}
}
}
}
2.2.2 doBind0方法的具體實現
doBind0
中也是講bind任務放到eventLoop中
跟進bind源碼,他會讓pipeline去實現bind方法,我們直接進入pipelinde的head
找到bind方法
出現下面的代碼:
@Override
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.");
}
// 查看當前channel是否已經活躍
boolean wasActive = isActive();
try {
// 完成bind
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
// bind完成以後完成激活channel
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
// 激活channel
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
2.2.2.1 追蹤bind方法
最後使用jdk的bind方法完成bind
2.2.2.2 pipeline.fireChannelActive();如何激活channel
不斷跟進readIfIsAutoRead()
,發現使用的selectKey的ops操作。
總結:服務啓動的核心步驟
一些知識點總結: