1. 程式人生 > >Netty源碼學習(五)ChannelInitializer

Netty源碼學習(五)ChannelInitializer

tty 用戶 bst tran 之前 warnings run 相關 fab

0. ChannelInitializer簡介

直接用ChannelInitializer的註釋吧:A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its eventLoop.

1. ChannelInitializer類圖

技術分享

需要註意的是:

a. ChannelInitializer繼承於ChannelInboundHandler接口

b. ChannelInitializer是一個抽象類,不能直接使用

2. initChannel抽象方法

ChannelInitializer中聲明了一個名為initChannel的抽象方法:

/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
protected abstract void initChannel(C ch) throws Exception;

ChannelInitializer的實現類必須要重寫這個方法,這個方法在Channel被註冊到EventLoop的時候會被調用

3. ChannelInitializer什麽時候會被調用?

以ServerBootstrap啟動這一場景為例

在ServerBootstrap.init()方法中,負責accept新鏈接的Channel的pipeline被添加了一個ChannelInitializer

p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}

ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});

由於此時這個Channel還沒有被register到EventLoop,於是在addLast方法的調用鏈中,會給pipeline添加一個PendingHandlerAddedTask,其目的是在Channel被register到EventLoop的時候,觸發一個回調事件

然後在AbstractBootstrap.initAndRegister()方法中,這個Channel會被register到boss EventLoopGoup,接著會被register到boss EventLoopGoup中的某一個具體的EventLoop

在AbstractChannel.register0()方法中,之前註冊的PendingHandlerAddedTask會被調用,經過一系列調用之後,ChannelInitializer.handleAdded()方法會被觸發:

    /**
     * {@inheritDoc} If override this method ensure you call super!
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // This should always be true with our current DefaultChannelPipeline implementation.
            // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
            // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
            // will be added in the expected order.
            initChannel(ctx);
        }
    }

    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
            try {
                initChannel((C) ctx.channel());//調用子類重寫的initChannel方法
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                remove(ctx);//將ChannelInitializer從pipeline中移除
            }
            return true;
        }
        return false;
    }


    /**
     * This method will be called once the {@link Channel} was registered. After the method returns this instance
     * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
     *
     * @param ch            the {@link Channel} which was registered.
     * @throws Exception    is thrown if an error occurs. In that case it will be handled by
     *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
     *                      the {@link Channel}.
     */
    protected abstract void initChannel(C ch) throws Exception;


    private void remove(ChannelHandlerContext ctx) {
        try {
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        } finally {
            initMap.remove(ctx);
        }
    }

大概意思是:

a. 觸發ChannelInitializer的initChannel方法,執行子類定義的一系列操作(在ServerBootstrap這個例子中就是將ServerBootstrapAcceptor註冊到pipeline中)

b. 將ChannelInitializer從pipeline中移除

4. 總結

ChannelInitializer的主要目的是為程序員提供了一個簡單的工具,用於在某個Channel註冊到EventLoop後,對這個Channel執行一些初始化操作。ChannelInitializer雖然會在一開始會被註冊到Channel相關的pipeline裏,但是在初始化完成之後,ChannelInitializer會將自己從pipeline中移除,不會影響後續的操作。

使用場景:

a. 在ServerBootstrap初始時,為監聽端口accept事件的Channel添加ServerBootstrapAcceptor

b. 在有新鏈接進入時,為監聽客戶端read/write事件的Channel添加用戶自定義的ChannelHandler

Netty源碼學習(五)ChannelInitializer