1. 程式人生 > 實用技巧 >Netty服務端啟動(一)——建立和初始化channel

Netty服務端啟動(一)——建立和初始化channel

以netty的一個小demo為例(使用的原始碼版本為4.1.50.Final)

b.bind(PORT)跟進

最終呼叫到AbstractBootstrap#doBind這個方法

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return
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); 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; } }

這裡就是服務端建立的主要程式碼

建立Channel

主要概括如下

1.通過demo裡傳入的NioServerSocketChannel類反射建立channel

2.在NioServerSocketChannel的建構函式裡

  • 通過java底層的SelectorProvider建立ServerSocketChannel
  • 呼叫父類建構函式設定阻塞模式,建立id,unsafe,pipeline等
  • 建立NioServerSocketChannelConfig,便於進行引數配置

方法體裡第一行程式碼的initAndRegister方法就是在建立和初始化channel

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel();   //建立channel,本節分析
        init(channel);
    } catch (Throwable t) {
        //省略
    }

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

建立channel就在channelFactory.newChannel()這行程式碼裡,這裡會呼叫到ReflectiveChannelFactory#newChannel這個方法

public T newChannel() {
    try {
        return constructor.newInstance();
    } catch (Throwable t) {
        //省略
    }
}

可以看到這裡用反射建立一個channel,也就是說channel是通過channelFactoryconstructor來反射建立的,而channelFactory是在demo裡呼叫channel方法時初始化的,如下

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)   //在這裡初始化了`channelFactory
    //省略

AbstractBootstrap#channel方法裡

public B channel(Class<? extends C> channelClass) {
    return channelFactory(new ReflectiveChannelFactory<C>(
        ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
}

最終呼叫到AbstractBootstrap#channelFactory方法裡

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    ObjectUtil.checkNotNull(channelFactory, "channelFactory");
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }

    this.channelFactory = channelFactory;
    return self();
}

既然這裡是通過反射建立了一個NioServerSocketChannel,接下來看看NioServerSocketChannel這個類的建構函式

public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));  
}

先看看NioServerSocketChannel#newSocket這個方法

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel(); //provider就是DEFAULT_SELECTOR_PROVIDER,為SelectorProvider.provider()
    } catch (IOException e) {
        //省略
    }
}

上面的建構函式呼叫到了另一個建構函式

public NioServerSocketChannel(ServerSocketChannel channel) {
    //注意這裡傳入SelectionKey.OP_ACCEPT,用於之後註冊accept事件
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

super方法呼叫父類做一些簡單的初始化,主要在AbstractNioChannelAbstractChannel這兩個類這種

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
        ch.configureBlocking(false);
    } catch (IOException e) {
        //省略
    }
}
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

NioServerSocketChannelConfig則是傳入java底層的ServerSocketChannel方便以後對做一些配置

初始化channel

先概括一下,主要做了這幾件事

  • 將使用者設定的options和attrs設定到channel的config裡
  • 向channel的pipeline裡新增使用者傳入的handler
  • 建立一個特殊的handler即ServerBootstrapAcceptor(用於處理新連線接入)並新增進pipeline裡

接下來分析初始化channel的過程,即init(channel)這行程式碼

這裡會呼叫到ServerBootstrap#init這個方法

void init(Channel channel) {
    //newOptionsArray這個方法拿到使用者設定的options設定到channel的config裡
    setChannelOptions(channel, newOptionsArray(), logger);
    //attrs0這個方法拿到使用者設定的attrs設定到channel裡
    setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));

    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    //拿到用於配置childHandler的childOptions
    synchronized (childOptions) {
        currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
    }
    //拿到用於配置childHandler的childAttrs
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);

    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            //拿到demo裡呼叫childHandler方法時新增的handler
            ChannelHandler handler = config.handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }

            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    //ServerBootstrapAcceptor是一個特殊的handler,用於處理新連線接入
                    pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

下篇 Netty服務端啟動(二)——註冊selector和埠繫結