netty 服務端的啓動過程

服務端的啓動過程

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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章