服務端的啓動過程
ServerBootstrap和Bootstrap的類圖如下所示:
可以看到ServerBootstrap和Bootstrap都是繼承自抽象類AbstractBootstrap。因爲 ServerBootstrap 和 Bootstrap 大部分的方法和職責都是相同的,所以公共的邏輯由AbstractBootstrap類實現。
本文僅分享 ServerBootstrap 啓動 Netty 服務端的過程
服務端啓動代碼如下:
// 創建 boss 線程組 用於服務端接受客戶端的連接
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 創建 worker 線程組 用於進行 SocketChannel 的數據讀寫
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 創建 EchoServerHandler 對象
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
// 創建 ServerBootstrap 對象
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 設置使用的 EventLoopGroup
.channel(NioServerSocketChannel.class) // 設置要被實例化的爲 NioServerSocketChannel 類
.option(ChannelOption.SO_BACKLOG, 100) // 設置 NioServerSocketChannel 的可選項
.handler(new LoggingHandler(LogLevel.INFO)) // 設置 NioServerSocketChannel 的處理器
.childHandler(new ChannelInitializer<SocketChannel>() {
//設置連入服務端的 Client 的 SocketChannel 的處理器
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(serverHandler);
}
});
// 綁定端口,並同步等待成功,即啓動服務端
ChannelFuture f = b.bind(8090).sync();
// 監聽服務端關閉,並阻塞等待
f.channel().closeFuture().sync();
} finally {
// 優雅關閉兩個 EventLoopGroup 對象
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
服務端啓動代碼主要包含兩塊邏輯:
1、設置啓動器類(ServerBootstrap)的屬性
2、綁定端口,啓動服務端
1、設置啓動器類(ServerBootstrap)的屬性
1.1、設置線程組
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);//設置boss線程組
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;//設置work線程組
return this;
}
1.2、設置服務端Channel
channel(Class<? extends C> channelClass)方法的主要邏輯是創建一個Channel實例的反射工廠,並將該反射工廠實例設置到ServerBootstrap的channelFactory屬性中。
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {//合法性校驗
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
//反射工廠類,簡單工廠
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
//設置反射工廠
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
//重載方法
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;//將反射工廠類設置到ServerBootstrap的channelFactory屬性
return self();
}
1.3、設置服務端通道的可選項
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return self();
}
1.4、設置服務端通道的處理器
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return self();
}
1.5、設置連入服務端的客戶端連接的處理器
public ServerBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
1.6 設置啓動器類(ServerBootstrap)的屬性小結
1、因爲ServerBootstrap類的屬性衆多,故netty的作者使用了建造者設計模式消除構造函數參數過多的問題
2、有關ServerBootstrap屬性設置的代碼大多數都是直接給成員變量賦值,沒有複雜的邏輯
2、綁定端口,啓動服務端
因爲綁定端口是ServerBootstrap和bootstrap共有的Api,故由其父類提供實現
調用父類AbstractBootstrap的bind(int inetPort)方法
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
// 調用重載的bind(SocketAddress localAddress)方法
public ChannelFuture bind(SocketAddress localAddress) {
validate();//合法性校驗
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
//調用doBind(final SocketAddress localAddress)方法
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化並註冊,返回一個ChannelFuture對象。因爲是異步執行,所以ChannelFuture可以理解爲一個執行結果的
//佔位符,在將來的某個時刻可以獲得執行結果
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
//因爲initAndRegister()方法是異步的,所以執行到下面代碼時通道的註冊和初始化工作有可能尚未完成,
//故需要對此進行分類討論
//如果此時 初始化和註冊 已完成,則執行執行綁定端口方法
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
//如果此時初始化和註冊操作未完成,則創建promise,並設置監聽器,等待初始化和註冊操作完成後的回調
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
//操作完成後回調,如果初始化和註冊的過程中未發生任何異常,則會調用doBind0方法執行綁定操作
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {//如果拋出異常
promise.setFailure(cause);
} else {//未拋出異常則執行綁定操作
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
可以看到doBind方法邏輯爲:
1、初始化並註冊通道
2、綁定到指定的端口
接下來對上述兩個步驟進行詳細分析
2.1初始化並註冊通道
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//調用1.2中設置的channel反射工廠創建對應channel的實例,在這裏就是創建NioServerSocketChannel
channel = channelFactory.newChannel();
init(channel);//初始化channel
} catch (Throwable t) {
if (channel != null) {
channel.unsafe().closeForcibly();//強制關閉
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
//初始化完成後將當前通道實例註冊到對應線程組(EventLoopGroup)中的線程(EventLoop)中
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
initAndRegister()方法的大致邏輯爲:
1、創建並初始化Channel實例
2、註冊 Channel 到 EventLoopGroup 中
2.1.1創建並初始化Channel實例
因爲服務端ServerBootstrap和客戶端Bootstrap的初始化邏輯不同,故init的邏輯由子類實現,AbstractBootstrap類只提供抽象方法。
@Override
void init(Channel channel) throws Exception {
//設置ChannelOption
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>() {
//在Channel註冊完成會被回調
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 添加用戶配置的 ChannelHandler 到 pipeline 中。
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//添加 ServerBootstrapAcceptor到pipeline
//確保是由channel綁定的eventLoop來執行
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
2.1.2 註冊 Channel 到 EventLoopGroup中
通過config().group() 獲取boss線程組,調用register(Channel channel)方法完成註冊
ChannelFuture regFuture = config().group().register(channel);
調用MultithreadEventLoopGroup的register(Channel channel)方法
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
next()方法會調用父類的事件執行選擇器EventExecutorChooser在線程組中選擇一個EventLoop執行註冊操作
調用SingleThreadEventLoop#register(Channel channel)方法執行註冊
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
//調用重載的register方法
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
//調用Channel的Unsafe的具體實現類(AbstractChannel類的內部類AbstractUnsafe)完成註冊
promise.channel().unsafe().register(this, promise);
return promise;
}
AbstractChannel類的內部類AbstractUnsafe的register(EventLoop eventLoop, final ChannelPromise promise)方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
//判斷當前channel是否已經被註冊過,如果已經註冊過則拋出非法狀態異常
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
//類型校驗,校驗 Channel 和 eventLoop 匹配,此時應該是NioEventLoop的子類
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
//如果當前線程是channel綁定的EventLoop,則執行註冊。否則,調用對應eventLoop執行
//因爲當前是服務端的啓動,所以當前線程一定是Main線程,故會將註冊任務交給綁定的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);
}
}
}
private void register0(ChannelPromise promise) {
try {
//校驗當前通道是否處於打開狀態
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
//調用底層jdk的api將通道實例註冊到對應的Selector上
doRegister();
neverRegistered = false;
registered = true;
//如果是第一次註冊的話,則會執行一遍pipeline中的所有handler
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
//觸發通道註冊事件
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
//實際註冊的方法
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//調用jdk的Channel實例註冊到選擇器中,並且聲明感興趣的事件爲 0
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
DefaultChannelPipeline的invokeHandlerAddedIfNeeded()方法
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();//當前線程必須是channel綁定的eventLoop
if (firstRegistration) {
firstRegistration = false;
//回調所有已經添加到pipeline中的Handler
callHandlerAddedForAllHandlers();
}
}
觸發通道註冊事件DefaultChannelPipeline#fireChannelRegistered(),在本例中主要作用就是調用LoggingHandler類打印日誌
@Override
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
2.2綁定到指定的端口
初始化和註冊通道的邏輯完成後接着會執行綁定端口操作,啓動服務端
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//調用channel通道對應的eventLoop執行綁定方法
//此處調用channel.eventLoop()執行任務的原因是,此處代碼會在register0()方法中的 //pipeline.fireChannelRegistered()方法之前執行,
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(SocketAddress localAddress, ChannelPromise promise)
—>DefaultChannelPipeline#bind(SocketAddress localAddress, ChannelPromise promise)
—>AbstractChannelHandlerContext#bind(final SocketAddress localAddress, final ChannelPromise promise)
—>AbstractChannelHandlerContext#invokeBind(SocketAddress localAddress, ChannelPromise promise)
—>LoggingHandler#bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
—>AbstractChannelHandlerContext#bind(final SocketAddress localAddress, final ChannelPromise promise)
—>AbstractChannelHandlerContext#invokeBind(SocketAddress localAddress, ChannelPromise promise)
—>DefaultChannelPipeline#bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
—>AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise)
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();//必須是和channel相關聯的Eventloop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
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();//當前Channel是否綁定對應端口
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//如果成功綁定指定端口,則觸發通道Active事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
//觸發通道Active事件
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
/**
* 調用jdk底層的Channel對象綁定指定端口
*
*/
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
觸發通道Active事件
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
AbstractChannelHandlerContext.invokeChannelActive(head);方法最終會調用AbstractUnsafe的beginRead()方法
@Override
public final void beginRead() {
// 判斷是否在 EventLoop 的線程中。
assertEventLoop();
// Channel 必須激活
if (!isActive()) {
return;
}
// 執行開始讀取
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// AbstractNioMessageChannel.java
@Override
protected void doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
super.doBeginRead();
}
// AbstractNioChannel.java
@Override
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps(); //此時interestOps應該爲0
if ((interestOps & readInterestOp) == 0) {
//修改當前selector感興趣的事件爲Accept事件
//也就說,設置完成後服務端就可以開始處理客戶端的連接事件了
selectionKey.interestOps(interestOps | readInterestOp);
}
}