Netty系列:NioServerSocketChannel源码分析

版权声明:作者:TheLudlows 载请注明出处: https://blog.csdn.net/TheLudlows/article/details/82588025

1.准备ChannelFactory

Netty服务端的ServerBootstrap引导类的channel方法来指定channel类型,并且在bind调用后,会调用此工厂对象来生成一个新channel。

// 设置并绑定服务端Channel,指定所使用的NIO传输的Channel
.channel(NioServerSocketChannel.class)

channel方法的内部细节如下代码:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

public ReflectiveChannelFactory(Class<? extends T> clazz) {
    if (clazz == null) {
        throw new NullPointerException("clazz");
    }
    this.clazz = clazz;
}

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;
    return self();
}

首先明确一点,channel方法是在AbstractBootstrap中,也就是说这个方法client和server端调用时一样的效果,只是参数不同而已,也就是最终实例化的channel类型不同。

接下来分析这段代码:这段代码其实就是设置了AbstractBootstrap中的channelFactory属性,并且实例化好的ReflectiveChannelFactory对象中有一个Class的属性被设置为我们参数NioServerSocketChannel。为后面的实例化NioServerSocketChannel类做准备。

2. 在哪实例化NioServerSocketChannel?

在启动过程的文章中也讲到过,实例化是在AbstractBootstrap中的initAndRegister()方法中:

channel = channelFactory.newChannel();
此处用到的channelFactory正式我们在第一节中讲到的实例化的ReflectiveChannelFactory实例,newChannel方法如下:

    public T newChannel() {
        try {
            return clazz.getConstructor().newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }

可以看出上面的代码通过反射生成NioServerSocketChannel实例,并且调用的是无参构造函数。

NioServerSocketChannel类的继承结构图如下所示:

netty

3. 构造函数链

NioServerSocketChanne的构造函数如下:

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

其中DEFAULT_SELECTOR_PROVIDER是一个静态的变量,

    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

看到Selector想起了NIO中的Selector,没错!默认的多路复用器就是这个。和NIO中的的selector获取方式一样。接下来是newSocket(DEFAULT_SELECTOR_PROVIDER)方法。

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException(
                "Failed to open a server socket.", e);
    }
}

通过newSocket创建ServerSocketChannel,回到无参的构造函数,接下来调用了另一个有参构造函数:

public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

主要有调用了父类的构造函数和设置config属性,下面将一步一步的分析。

接着分析,父类AbstractNioMessageChannel的构造方法如下:

    protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent, ch, readInterestOp);
    }

因为是服务端新生成的channel,第一个参数指定为null,表示没有父channel,第二个参数指定为ServerSocketChannel,第三个参数指定ServerSocketChannel关心的事件类型为SelectionKey.OP_ACCEPT。

续调用父类AbstractNioChannel的构造方法:

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        //将ServerSocketChannel对象保存
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            // 设置当前通道为非阻塞的
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }

在AbstractNioChannel中做了下面几件事:

  1. 通过this.ch = ch 保存ServerSocketChannel, 因为NioServerSocketChannel是Netty封装的对象,而ServerSocketChannel是有前面默认selector_provider生成的,是java nio的, 其实“this.ch = ch”可以被认为是绑定原生的ServerSocketChannel到NioServerSocketChannel对象中。
  2. 设置ServerSocketChannel关心的事件类型
  3. 设置ServerSocketChannel为非阻塞的

继续进入到父类AbstractChannel的构造函数:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

此构造方法中,主要做了三件事:

  1. 给channel生成一个新的id
  2. 通过newUnsafe初始化channel的unsafe属性
  3. newChannelPipeline初始化channel的pipeline属性

Id是通过当前时间以及Mac地址等生成的标识,此处不再赘述。主要分析newUnsafe和newChannelPipleLine方法。

4. newUnsafe();

io.netty.channel.Channel.Unsafesun.misc.Unsafe类似,我们知道后者是操作Java底层的API,前者是操作Netty底层的API,Netty底层就是NIO,说白了就是操作Channel,进行读和写等操作。

在AbstractChannel类中,newUnsafe()是一个抽象方法,通过上面的继承关系,AbstractNioMessageChannel类中有newUnsafe()的实现。

  private final class NioMessageUnsafe extends AbstractNioUnsafe {

        private final List<Object> readBuf = new ArrayList<Object>();

        @Override
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }

NioMessageUnsafe 只覆盖了 父类AbstractNioUnsafe中的read方法,其实unsafe对象是真正的负责底层channel的连接/读/写等操作的,unsafe就好比一个底层channel操作的代理对象。

5. newChannelPipeline();

newChannelPipeline直接在AbstractChannel内实现,该方法返回了DefaultChannelPipeline对象

    protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }
    //构造方法
     protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);

        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

此DefaultChannelPipeline对象会绑定NioServerSocketChannel对象,并初始化了HeadContext及TailContext对象。head及tail初始化完成后,它们会相互连接。pipleLine的更多细节在下篇源码分析中分享。

父类的构造函数执行完毕之后,继续config = new NioServerSocketChannelConfig(this, javaChannel().socket());此config对象就是就会对底层ServerSocket一些配置设置行为的封装。

小结
  1. NioServerSocketChannel对象内部绑定了Java NIO创建的ServerSocketChannel对象;

  2. Netty中,每个channel都有一个unsafe对象,此对象封装了Java NIO底层channel的操作细节;

  3. Netty中,每个channel都有一个pipeline对象,此对象就是一个双向链表;

猜你喜欢

转载自blog.csdn.net/TheLudlows/article/details/82588025
今日推荐