Netty4.x 源碼實戰系列(三):NioServerSocketChannel全剖析

根據上一篇《Netty4.x 源碼實戰系列(二):服務端bind流程詳解》所述,在進行服務端開發時,必須經過ServerBootstrap引導類的channel方法來指定channel類型, channel方法的調用其實就是實例化了一個用於生成此channel類型對象的工廠對象。 而且在bind調用後,會調用此工廠對象來生成一個新channel。java

本篇將經過NioServerSocketChannel實例化過程,來深刻剖析NioServerSocketChannel。segmentfault

在開始代碼分析以前,咱們先看一下NioServerSocketChannel的類繼承結構圖:
圖片描述socket

調用工廠完成NioServerSocketChannel實例的建立

在綁定偵聽端口過程當中,咱們調用了AbstractBootstrap的initAndRegister方法來完成channel的建立與初始化,channel實例化代碼以下:ide

channelFactory.newChannel()

而channelFactory對象是咱們經過ServerBootstrap.channel方法的調用生成的oop

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

經過代碼可知,此工廠對象是ReflectiveChannelFactory實例this

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);
        }
    }
}

因此 channelFactory.newChannel() 實例化其實就是NioServerSocketChannel無參構造方法反射而成。spa

NioServerSocketChannel實例化過程分析

咱們先看一下NioServerSocketChannel的無參構造代碼代理

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

無參構造方法中有兩個關鍵點:
一、使用默認的多路複用器輔助類 DEFAULT_SELECTOR_PROVIDERrest

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

二、經過newSocket建立ServerSocketChannelnetty

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對象繼續傳遞給本類中的NioServerSocketChannel(ServerSocketChannel channel)構造方法

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

在其內部,咱們會調用父類AbstractNioMessageChannel的構造方法:

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

由於是服務端新生成的channel,第一個參數指定爲null,表示沒有父channel,第二個參數指定爲ServerSocketChannel,第三個參數指定ServerSocketChannel關心的事件類型爲SelectionKey.OP_ACCEPT。

在AbstractNioMessageChannel內部會繼續調用父類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中作了下面幾件事:
一、繼續調用父類AbstractChannel(Channel parent)構造方法;
二、經過this.ch = ch 保存ServerSocketChannel, 由於NioServerSocketChannel是Netty封裝的對象,而ServerSocketChannel是有前面默認selector_provider生成的,是java nio的, 其實「this.ch = ch」能夠被認爲是綁定java nio服務端通道至netty對象中;
三、設置ServerSocketChannel關心的事件類型;
四、設置ServerSocketChannel爲非阻塞的(熟悉Java NIO的都知道若是不設置爲false,啓動多路複用器會報異常)

咱們再看一下AbstractChannel(Channel parent)的內部代碼細節

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

此構造方法中,主要作了三件事:
一、給channel生成一個新的id
二、經過newUnsafe初始化channel的unsafe屬性
三、newChannelPipeline初始化channel的pipeline屬性

id的生成咱們就不細究了,咱們主要看看newUnsafe 及 newChannelPipeline是如何建立unsafe對象及pipeline對象的。

newUnsafe()方法調用
在AbstractChannel類中,newUnsafe()是一個抽象方法

protected abstract AbstractUnsafe newUnsafe();

經過上面的類繼承結構圖,咱們找到AbstractNioMessageChannel類中有newUnsafe()的實現

@Override
protected AbstractNioUnsafe newUnsafe() {
    return new NioMessageUnsafe();
}

此方法返回一個NioMessageUnsafe實例對象,而NioMessageUnsafe是AbstractNioMessageChannel的內部類

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方法,經過NioMessageUnsafe 及其父類的代碼即可以知道, 其實unsafe對象是真正的負責底層channel的鏈接/讀/寫等操做的,unsafe就比如一個底層channel操做的代理對象。

newChannelPipeline()方法調用
newChannelPipeline直接在AbstractChannel內實現

protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

該方法返回了建立了一個DefaultChannelPipeline對象

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對象。

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

head及tail初始化完成後,它們會相互鏈接。

經過上面的代碼能夠得出,pipeline就是一個雙向鏈表。關於Pipeline的更多細節,此處不作贅述,歡迎你們關注下一篇文章。

咱們在回到NioServerSocketChannel的構造方法 NioServerSocketChannel(ServerSocketChannel channel)

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

父類構造方法調用完成後,NioServerSocketChannel還要初始化一下本身的配置對象

config = new NioServerSocketChannelConfig(this, javaChannel().socket());

NioServerSocketChannelConfig是NioServerSocketChannel的內部類

private final class NioServerSocketChannelConfig extends DefaultServerSocketChannelConfig {
    private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel, javaSocket);
    }

    @Override
    protected void autoReadCleared() {
        clearReadPending();
    }
}

而NioServerSocketChannelConfig 又是繼承自DefaultServerSocketChannelConfig,經過代碼分析,此config對象就是就會對底層ServerSocket一些配置設置行爲的封裝。

至此NioServerSocketChannel對象應該建立完成了~

總結:

一、NioServerSocketChannel對象內部綁定了Java NIO建立的ServerSocketChannel對象;

二、Netty中,每一個channel都有一個unsafe對象,此對象封裝了Java NIO底層channel的操做細節;

三、Netty中,每一個channel都有一個pipeline對象,此對象就是一個雙向鏈表;

相關文章
相關標籤/搜索