Netty源碼筆記

Netty版本4.0.29.Final,以構(gòu)造客戶端連接服務(wù)端的角度來追蹤源碼

一 創(chuàng)建Netty事件循環(huán)組

NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

NioEventLoopGroup的構(gòu)造器中會(huì)調(diào)用父類MultithreadEventLoopGroup的構(gòu)造器

  1. SelectorProvider.provider()返回運(yùn)行JVM的操作系統(tǒng)提供的provider
  2. 如果沒有指定線程數(shù)量,默認(rèn)為兩倍核數(shù)
    // NioEventLoopGroup
    public NioEventLoopGroup() {
        this(0);
    }
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, null);
    }
    public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
        this(nThreads, threadFactory, SelectorProvider.provider());
    }
    public NioEventLoopGroup(
            int nThreads, ThreadFactory threadFactory, final SelectorProvider selectorProvider) {
        super(nThreads, threadFactory, selectorProvider);
    }

    // MultithreadEventLoopGroup
    private static final int DEFAULT_EVENT_LOOP_THREADS;

    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));

        if (logger.isDebugEnabled()) {
            logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
        }
    }

    protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        super(nThreads == 0? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
    }

在父類MultithreadEventExecutorGroup的構(gòu)造器中

  1. 創(chuàng)建默認(rèn)的線程工廠,后續(xù)用來創(chuàng)建線程
  2. 創(chuàng)建一個(gè)SingleThreadEventExecutor數(shù)組,數(shù)組大小為兩倍核數(shù),保存在屬性children上
  3. 創(chuàng)建選擇器保存在屬性chooser上
  4. 在newChild方法中創(chuàng)建EventExecutor填充到數(shù)組上
  5. newChild是父類MultithreadEventExecutorGroup提供的一個(gè)抽象方法,由于當(dāng)前是在創(chuàng)建NioEventLoopGroup對(duì)象,具體實(shí)現(xiàn)還是由NioEventLoopGroup完成。
  6. 可以看到NioEventLoopGroup中完成了NioEventLoop的創(chuàng)建
    // MultithreadEventExecutorGroup
    protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (threadFactory == null) {
            threadFactory = newDefaultThreadFactory();
        }

        children = new SingleThreadEventExecutor[nThreads];
        if (isPowerOfTwo(children.length)) {
            chooser = new PowerOfTwoEventExecutorChooser();
        } else {
            chooser = new GenericEventExecutorChooser();
        }

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(threadFactory, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }
    }

    // NioEventLoopGroup
    @Override
    protected EventExecutor newChild(
            ThreadFactory threadFactory, Object... args) throws Exception {
        return new NioEventLoop(this, threadFactory, (SelectorProvider) args[0]);
    }

二 創(chuàng)建Netty事件循環(huán)

  1. 將一開始創(chuàng)建的provider保存在屬性provider上
  2. openSelector中返回操作系統(tǒng)提供的selector(多路復(fù)用器)
  3. 如果未禁用優(yōu)化(默認(rèn)未禁用),使用反射將操作系統(tǒng)返回的selector中的屬性selectedKeys和publicSelectedKeys改為可寫,并用Netty的SelectedSelectionKeySet代替
  4. 最后再看父類SingleThreadEventExecutor構(gòu)造器
    // NioEventLoop
    NioEventLoop(NioEventLoopGroup parent, ThreadFactory threadFactory, SelectorProvider selectorProvider) {
        super(parent, threadFactory, false);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        provider = selectorProvider;
        selector = openSelector();
    }

    private Selector openSelector() {
        final Selector selector;
        try {
            selector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }

        if (DISABLE_KEYSET_OPTIMIZATION) {
            return selector;
        }

        try {
            SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

            Class<?> selectorImplClass =
                    Class.forName("sun.nio.ch.SelectorImpl", false, PlatformDependent.getSystemClassLoader());

            // Ensure the current selector implementation is what we can instrument.
            if (!selectorImplClass.isAssignableFrom(selector.getClass())) {
                return selector;
            }

            Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
            Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

            selectedKeysField.setAccessible(true);
            publicSelectedKeysField.setAccessible(true);

            selectedKeysField.set(selector, selectedKeySet);
            publicSelectedKeysField.set(selector, selectedKeySet);

            selectedKeys = selectedKeySet;
            logger.trace("Instrumented an optimized java.util.Set into: {}", selector);
        } catch (Throwable t) {
            selectedKeys = null;
            logger.trace("Failed to instrument an optimized java.util.Set into: {}", selector, t);
        }

        return selector;
    }
  1. 事件循環(huán)組保存在屬性parent上。對(duì)于客戶端來說,這里的parent還是開始創(chuàng)建的NioEventLoopGroup(服務(wù)端可以有兩個(gè)具有父子關(guān)系的事件循環(huán)組)
  2. 使用線程工廠創(chuàng)建線程,封裝了匿名任務(wù)(后續(xù)會(huì)分析該任務(wù)),保存在屬性thread上(未啟動(dòng))
  3. 使用NioEventLoop的newTaskQueue創(chuàng)建一個(gè)無鎖并發(fā)的單消費(fèi)者多生產(chǎn)者隊(duì)列,保存在屬性taskQueue上
    // SingleThreadEventExecutor
    protected SingleThreadEventExecutor(
            EventExecutorGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {

        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }

        this.parent = parent;
        this.addTaskWakesUp = addTaskWakesUp;

        thread = threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                // 暫時(shí)不關(guān)注任務(wù)內(nèi)邏輯 省略
            }
        });

        taskQueue = newTaskQueue();
    }

    // NioEventLoop
    @Override
    protected Queue<Runnable> newTaskQueue() {
        // This event loop never calls takeTask()
        return PlatformDependent.newMpscQueue();
    }

    // PlatformDependent
    public static <T> Queue<T> newMpscQueue() {
        return new MpscLinkedQueue<T>();
    }

上面已經(jīng)完成了NioEventLoop的創(chuàng)建,并保存在NioEventLoopGroup的數(shù)組屬性上。而關(guān)于NioEventLoop在具備線程池能力時(shí),在何時(shí)啟動(dòng)已經(jīng)創(chuàng)建的線程呢?在SingleThreadEventExecutor::execute中可以找到答案

  1. 當(dāng)任務(wù)被提交到NioEventLoop以后,首先判斷提交任務(wù)的線程與thread屬性保存的線程是否是同一個(gè)
  2. 如果不是同一個(gè),startThread中根據(jù)維護(hù)的STATE_UPDATER來判斷當(dāng)前NioEventLoop是否已經(jīng)啟動(dòng)過,如果沒有,使用CAS更新該NioEventLoop狀態(tài)為已經(jīng)啟動(dòng)
  3. 然后啟動(dòng)該線程,后續(xù)如果再執(zhí)行該方法,由于線程已經(jīng)啟動(dòng),方法內(nèi)什么都不會(huì)做
  4. 再回到execute中,addTask將任務(wù)生產(chǎn)到之前創(chuàng)建的隊(duì)列中
    // SingleThreadEventExecutor
    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }
        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }
    // AbstractEventExecutor
    @Override
    public boolean inEventLoop() {
        return inEventLoop(Thread.currentThread());
    }

    // SingleThreadEventExecutor
    @Override
    public boolean inEventLoop(Thread thread) {
        return thread == this.thread;
    }
    // SingleThreadEventExecutor
    private void startThread() {
        if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                schedule(new ScheduledFutureTask<Void>(
                        this, Executors.<Void>callable(new PurgeTask(), null),
                        ScheduledFutureTask.deadlineNanos(SCHEDULE_PURGE_INTERVAL), -SCHEDULE_PURGE_INTERVAL));
                thread.start();
            }
        }
    }
    // SingleThreadEventExecutor
    protected void addTask(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        if (isShutdown()) {
            reject();
        }
        taskQueue.add(task);
    }

而關(guān)于NioEventLoop內(nèi)線程啟動(dòng)后的邏輯,可以在創(chuàng)建該線程時(shí)看到有向線程提交一個(gè)任務(wù)。根據(jù)任務(wù)內(nèi)SingleThreadEventExecutor.this.run()可以定位到創(chuàng)建線程提交任務(wù)的NioEventLoop::run

        // SingleThreadEventExecutor
        thread = threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                boolean success = false;
                updateLastExecutionTime();
                try {
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                    for (;;) {
                        int oldState = STATE_UPDATER.get(SingleThreadEventExecutor.this);
                        if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
                                SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
                            break;
                        }
                    }
                    // Check if confirmShutdown() was called at the end of the loop.
                    if (success && gracefulShutdownStartTime == 0) {
                        logger.error(
                                "Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
                                SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
                                "before run() implementation terminates.");
                    }

                    try {
                        // Run all remaining tasks and shutdown hooks.
                        for (;;) {
                            if (confirmShutdown()) {
                                break;
                            }
                        }
                    } finally {
                        try {
                            cleanup();
                        } finally {
                            STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
                            threadLock.release();
                            if (!taskQueue.isEmpty()) {
                                logger.warn(
                                        "An event executor terminated with " +
                                        "non-empty task queue (" + taskQueue.size() + ')');
                            }

                            terminationFuture.setSuccess(null);
                        }
                    }
                }
            }
        });

NioEventLoop::run是Netty的核心所在。它是NioEventLoop的線程執(zhí)行的唯一任務(wù)。方法內(nèi)無限循環(huán),阻塞等待IO事件或隊(duì)列中的任務(wù)

  1. 判斷NioEventLoop的隊(duì)列中是否有任務(wù)
  2. 如果有,執(zhí)行一次selectNow,這會(huì)導(dǎo)致多路復(fù)用器上準(zhǔn)備就緒的事件被分配到selectedKeys上(jdk nio基礎(chǔ),操作系統(tǒng)介入)
  3. 如果沒有,執(zhí)行select(oldWakenUp);,阻塞oldWakenUp時(shí)間后,開始下一個(gè)循環(huán)(阻塞期間等待多路復(fù)用器上準(zhǔn)備就緒的事件被分配到selectedKeys上)
  4. 根據(jù)設(shè)置的IO事件的執(zhí)行比例(默認(rèn)50%),計(jì)算出執(zhí)行隊(duì)列任務(wù)的最長執(zhí)行時(shí)間(先記錄IO事件執(zhí)行的總耗時(shí),然后根據(jù)比例,計(jì)算出執(zhí)行隊(duì)列任務(wù)的最長執(zhí)行時(shí)間)
    // NioEventLoop
    @Override
    protected void run() {
        for (;;) {
            boolean oldWakenUp = wakenUp.getAndSet(false);
            try {
                if (hasTasks()) {
                    selectNow();
                } else {
                    select(oldWakenUp);

                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                }

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    processSelectedKeys();
                    runAllTasks();
                } else {
                    final long ioStartTime = System.nanoTime();

                    processSelectedKeys();

                    final long ioTime = System.nanoTime() - ioStartTime;
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }

                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        break;
                    }
                }
            } catch (Throwable t) {
                logger.warn("Unexpected exception in the selector loop.", t);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }
執(zhí)行IO事件
  1. 在有IO事件就緒時(shí),處理for循環(huán)本次循環(huán)取出的這些事件
  2. 這里有看到unsafe類的介入,對(duì)于客戶端而言,可能存在的IO事件有建立連接的應(yīng)答,或者服務(wù)端主動(dòng)發(fā)送消息給客戶端
  3. 交由unsafe來處理的對(duì)應(yīng)方法分別為unsafe::finishConnect和unsafe::read
  4. 對(duì)unsafe的分析將在后面完成,這里暫時(shí)不展開
    // NioEventLoop
    private void processSelectedKeys() {
        if (selectedKeys != null) {
            processSelectedKeysOptimized(selectedKeys.flip());
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }

    private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
        for (int i = 0;; i ++) {
            final SelectionKey k = selectedKeys[i];
            if (k == null) {
                break;
            }
            // null out entry in the array to allow to have it GC'ed once the Channel close
            // See https://github.com/netty/netty/issues/2363
            selectedKeys[i] = null;

            final Object a = k.attachment();

            if (a instanceof AbstractNioChannel) {
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }

            if (needsToSelectAgain) {
                // null out entries in the array to allow to have it GC'ed once the Channel close
                // See https://github.com/netty/netty/issues/2363
                for (;;) {
                    if (selectedKeys[i] == null) {
                        break;
                    }
                    selectedKeys[i] = null;
                    i++;
                }

                selectAgain();
                // Need to flip the optimized selectedKeys to get the right reference to the array
                // and reset the index to -1 which will then set to 0 on the for loop
                // to start over again.
                //
                // See https://github.com/netty/netty/issues/1523
                selectedKeys = this.selectedKeys.flip();
                i = -1;
            }
        }
    }

    private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            // close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());
            return;
        }

        try {
            int readyOps = k.readyOps();
            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
                if (!ch.isOpen()) {
                    // Connection already closed - no need to handle write.
                    return;
                }
            }
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }
執(zhí)行隊(duì)列任務(wù)

關(guān)于隊(duì)列中的任務(wù)從哪里來?一是源碼內(nèi)部,啟動(dòng)時(shí)會(huì)直接提交任務(wù)到隊(duì)列中;二是可以直接取出channel中的NioEventLoop向其提交任務(wù);三是使用Channel寫數(shù)據(jù)時(shí),都是以任務(wù)的形式提交到隊(duì)列中。與Channel綁定的NioEventLoop循環(huán)時(shí)會(huì)消費(fèi)提交到隊(duì)列中任務(wù)并執(zhí)行,后續(xù)在分析unsafe時(shí)會(huì)一并提及。

  1. IO事件執(zhí)行完成后,回到NioEventLoop::run中,在runAllTasks中開始執(zhí)行隊(duì)列中的任務(wù)
  2. pollTask從隊(duì)列中取出一個(gè)任務(wù),接著在for循環(huán)中執(zhí)行該任務(wù),并重復(fù)取出任務(wù)、執(zhí)行任務(wù)的過程
  3. 退出for循環(huán)的條件有兩個(gè),一是隊(duì)列中沒有需要執(zhí)行的任務(wù),二是到了執(zhí)行定時(shí)任務(wù)的時(shí)間
    // NioEventLoop
    protected boolean runAllTasks(long timeoutNanos) {
        fetchFromScheduledTaskQueue();
        Runnable task = pollTask();
        if (task == null) {
            return false;
        }

        final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
        long runTasks = 0;
        long lastExecutionTime;
        for (;;) {
            try {
                task.run();
            } catch (Throwable t) {
                logger.warn("A task raised an exception.", t);
            }

            runTasks ++;

            // Check timeout every 64 tasks because nanoTime() is relatively expensive.
            // XXX: Hard-coded value - will make it configurable if it is really a problem.
            if ((runTasks & 0x3F) == 0) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                if (lastExecutionTime >= deadline) {
                    break;
                }
            }

            task = pollTask();
            if (task == null) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                break;
            }
        }

        this.lastExecutionTime = lastExecutionTime;
        return true;
    }

三 客戶端Channel類型

客戶端Bootstrap配置引導(dǎo)時(shí),需要指定Channel類型,后續(xù)會(huì)使用反射創(chuàng)建該Channel類型實(shí)例。

  1. 假設(shè)以Channel類型為NioSocketChannel來跟蹤源碼
  2. 一系列構(gòu)造器之間的調(diào)用,將JDK原生的SocketChannel作為屬性保存在父類AbstractChannel的parent屬性上
  3. 創(chuàng)建NioSocketChannelUnsafe實(shí)例保存在父類AbstractChannel屬性u(píng)nsafe上
  4. 創(chuàng)建管道DefaultChannelPipeline保存在父類AbstractChannel屬性pipeline上
  5. 管道DefaultChannelPipeline中默認(rèn)擁有head和tail兩個(gè)ChannelHandler,構(gòu)成只有頭尾兩節(jié)點(diǎn)的雙向鏈表。
    // NioSocketChannel
    public NioSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    public NioSocketChannel(SocketChannel socket) {
        this(null, socket);
    }
    public NioSocketChannel(Channel parent, SocketChannel socket) {
        super(parent, socket);
        config = new NioSocketChannelConfig(this, socket.socket());
    }
    // AbstractNioByteChannel
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }
    // AbstractNioChannel
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        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);
        }
    }
    // AbstractChannel
    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        unsafe = newUnsafe();
        pipeline = new DefaultChannelPipeline(this);
    }
    // NioSocketChannel
    @Override
    protected AbstractNioUnsafe newUnsafe() {
        return new NioSocketChannelUnsafe();
    }
    // DefaultChannelPipeline
    public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;

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

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

四 客戶端建立連接

bootstrap.connect()

客戶端完成NioEventLoopGroup和Bootstrap的創(chuàng)建及對(duì)Bootstrap進(jìn)行相關(guān)的設(shè)置后,使用Bootstrap嘗試與服務(wù)端建立連接。在同步與異步之間,推薦使用異步來監(jiān)聽連接事件的結(jié)果。

  1. 一些必須的參數(shù)校驗(yàn)
  2. 由于是與服務(wù)器建立連接,localAddress值為null
  3. doConnect中先調(diào)用initAndRegister異步創(chuàng)建通道及注冊(cè)
  4. 無論是使用isDone還是使用監(jiān)聽器,都是在通道創(chuàng)建完成后,使用doConnect0進(jìn)行連接
    // Bootstrap
    public ChannelFuture connect() {
        validate();
        SocketAddress remoteAddress = this.remoteAddress;
        if (remoteAddress == null) {
            throw new IllegalStateException("remoteAddress not set");
        }

        return doConnect(remoteAddress, localAddress());
    }

    private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        final ChannelPromise promise = channel.newPromise();
        if (regFuture.isDone()) {
            doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
        } else {
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
                }
            });
        }

        return promise;
    }

AbstractBootstrap是Bootstrap的父類,initAndRegister中完成了通道的創(chuàng)建、初始化以及注冊(cè)

  1. 使用Bootstrap中默認(rèn)的BootstrapChannelFactory,根據(jù)設(shè)置的channel,使用反射創(chuàng)建channel實(shí)例
  2. 假設(shè)使用上述NioSocketChannel,當(dāng)然也可以是別的channel類型
  3. init負(fù)責(zé)對(duì)創(chuàng)建的channel初始化
  4. 初始化完成后異步完成注冊(cè)
    // AbstractBootstrap
    final ChannelFuture initAndRegister() {
        final Channel channel = channelFactory().newChannel();
        try {
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }

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

        // If we are here and the promise is not failed, it's one of the following cases:
        // 1) If we attempted registration from the event loop, the registration has been completed at this point.
        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
        // 2) If we attempted registration from the other thread, the registration request has been successfully
        //    added to the event loop's task queue for later execution.
        //    i.e. It's safe to attempt bind() or connect() now:
        //         because bind() or connect() will be executed *after* the scheduled registration task is executed
        //         because register(), bind(), and connect() are all bound to the same thread.

        return regFuture;
    }
Channel初始化
  1. init方法還是交由子類Bootstrap實(shí)現(xiàn)
  2. 取出channel上的管道,將客戶端自定義的handler添加進(jìn)去,底層其實(shí)是對(duì)雙向鏈表的變更,如DefaultChannelPipeline::addLast0所示,這時(shí)會(huì)形成head、自定義ChannelInitializer、tail三個(gè)節(jié)點(diǎn)
  3. 對(duì)channel的option和attr進(jìn)行設(shè)置,以支持將自定義參數(shù)配置到channel上
    // Bootstrap
    @Override
    @SuppressWarnings("unchecked")
    void init(Channel channel) throws Exception {
        ChannelPipeline p = channel.pipeline();
        p.addLast(handler());

        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
                try {
                    if (!channel.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
                        logger.warn("Unknown channel option: " + e);
                    }
                } catch (Throwable t) {
                    logger.warn("Failed to set a channel option: " + channel, t);
                }
            }
        }

        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }
    // DefaultChannelPipeline
    private void addLast0(final String name, AbstractChannelHandlerContext newCtx) {
        checkMultiplicity(newCtx);

        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;

        name2ctx.put(name, newCtx);

        callHandlerAdded(newCtx);
    }
Channel注冊(cè)

group()取到開始配置的NioEventLoopGroup,register在父類MultithreadEventLoopGroup中

  1. next()內(nèi)借助之前生成的chooser的next()方法,選出MultithreadEventExecutorGroup中child數(shù)組上的一個(gè)NioEventLoop
  2. 接著變成NioEventLoop::register方法,該方法來自父類SingleThreadEventLoop
  3. 在SingleThreadEventLoop中再轉(zhuǎn)換為channel上unsafe::register,關(guān)于unsafe暫時(shí)先不展開介紹。
    // MultithreadEventLoopGroup
    @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
    // MultithreadEventExecutorGroup
    @Override
    public EventExecutor next() {
        return chooser.next();
    }
    // GenericEventExecutorChooser
    private final class GenericEventExecutorChooser implements EventExecutorChooser {
        @Override
        public EventExecutor next() {
            return children[Math.abs(childIndex.getAndIncrement() % children.length)];
        }
    }
    // SingleThreadEventLoop
    @Override
    public ChannelFuture register(Channel channel) {
        return register(channel, new DefaultChannelPromise(channel, this));
    }
    @Override
    public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        if (promise == null) {
            throw new NullPointerException("promise");
        }

        channel.unsafe().register(this, promise);
        return promise;
    }
Channel管道初始化

雖說暫時(shí)不引入unsafe類邏輯,但在unsafe::register中,通道注冊(cè)完成后會(huì)調(diào)用管道的fireChannelRegistered方法,進(jìn)而執(zhí)行自定義的ChannelInitializer,最終形成完整的管道。
目前管道上鏈表有三個(gè)節(jié)點(diǎn):head、自定義ChannelInitializer、tail(規(guī)定自頭向尾的流向?yàn)镮n,自尾向頭的為Out)
它們的類型都是AbstractChannelHandlerContext,每個(gè)Context上都持有對(duì)應(yīng)的ChannelHandler

  1. 首先head執(zhí)行父類方法fireChannelRegistered,該方法找到以In流向的下一個(gè)AbstractChannelHandlerContext
  2. 然后在invokeChannelRegistered中找到Context持有的Handler,執(zhí)行Handler::channelRegistered
  3. 自定義的ChannelInitializer屬于ChannelInboundHandler的子類,流向?yàn)镮n。其channelRegistered方法執(zhí)行時(shí),調(diào)用被重寫的initChannel方法
  4. initChannel方法內(nèi)一般定義的是業(yè)務(wù)自身往管道添加Handler的邏輯ChannelPipeline::addLast
  5. addLast之前自定義ChannelInitializer已經(jīng)有分析過,目的是將ChannelHandler封裝為Context添加到管道上,并根據(jù)實(shí)現(xiàn)的接口標(biāo)記節(jié)點(diǎn)流向,最終形成完整的雙向鏈表
  6. 最后回到ChannelInitializer::channelRegistered中,將自定義的ChannelInitializer移出,因?yàn)樗恍枰赾hannel初始化時(shí)發(fā)揮作用,不需要存在于實(shí)際的管道中
    // DefaultChannelPipeline
    @Override
    public ChannelPipeline fireChannelRegistered() {
        head.fireChannelRegistered();
        return this;
    }
    // AbstractChannelHandlerContext
    @Override
    public ChannelHandlerContext fireChannelRegistered() {
        final AbstractChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRegistered();
        } else {
            executor.execute(new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeChannelRegistered();
                }
            });
        }
        return this;
    }

    private void invokeChannelRegistered() {
        try {
            ((ChannelInboundHandler) handler()).channelRegistered(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }

    // ChannelInitializer
    @Override
    @SuppressWarnings("unchecked")
    public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ChannelPipeline pipeline = ctx.pipeline();
        boolean success = false;
        try {
            initChannel((C) ctx.channel());
            pipeline.remove(this);
            ctx.fireChannelRegistered();
            success = true;
        } catch (Throwable t) {
            logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
        } finally {
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
            if (!success) {
                ctx.close();
            }
        }
    }
Channel連接

至此,Channel的初始化及注冊(cè)已經(jīng)完成。回到Bootstrap::doConnect中,在Channel注冊(cè)這個(gè)異步任務(wù)完成后,開始真正的連接服務(wù)端

  1. 獲取到Channel綁定的NioEventLoop,提交任務(wù)到線程池
  2. 任務(wù)被觸發(fā)時(shí),使用channel進(jìn)行連接
  3. 使用channel上的管道pipeline進(jìn)行連接,從tailf開始,以O(shè)ut的流向,找到下一個(gè)Out節(jié)點(diǎn),執(zhí)行其invokeConnect
  4. 最終流到head節(jié)點(diǎn),head內(nèi)繼續(xù)使用unsafe與服務(wù)端進(jìn)行連接的建立
    // Bootstrap類
    private static void doConnect0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    if (localAddress == null) {
                        channel.connect(remoteAddress, promise);
                    } else {
                        channel.connect(remoteAddress, localAddress, promise);
                    }
                    promise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

    // AbstractChannel
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return pipeline.connect(remoteAddress, promise);
    }
     // DefaultChannelPipeline
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return tail.connect(remoteAddress, promise);
    }
    // AbstractChannelHandlerContext
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return connect(remoteAddress, null, promise);
    }

    @Override
    public ChannelFuture connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

        if (remoteAddress == null) {
            throw new NullPointerException("remoteAddress");
        }
        if (!validatePromise(promise, false)) {
            // cancelled
            return promise;
        }

        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeConnect(remoteAddress, localAddress, promise);
        } else {
            safeExecute(executor, new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeConnect(remoteAddress, localAddress, promise);
                }
            }, promise, null);
        }

        return promise;
    }

    private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

    // HeadContext
    @Override
    public void connect(
            ChannelHandlerContext ctx,
            SocketAddress remoteAddress, SocketAddress localAddress,
            ChannelPromise promise) throws Exception {
        unsafe.connect(remoteAddress, localAddress, promise);
    }

五 客戶端發(fā)送數(shù)據(jù)

channel.write("test");

channel連接建立完成后,可以使用channel寫數(shù)據(jù)

  1. 使用channel寫數(shù)據(jù)時(shí),從channel開始,經(jīng)過管道,找到tail開始執(zhí)行寫數(shù)據(jù)邏輯
  2. tail作為雙向鏈表的尾部,使用自尾向頭的Out流向,找到下一個(gè)Context,直到最終流到head節(jié)點(diǎn)
  3. head節(jié)點(diǎn)轉(zhuǎn)化為unsafe::write,在后面分析
    // AbstractChannel
    @Override
    public ChannelFuture write(Object msg) {
        return pipeline.write(msg);
    }

    // DefaultChannelPipeline
    @Override
    public ChannelFuture write(Object msg) {
        return tail.write(msg);
    }

    // AbstractChannelHandlerContext
    @Override
    public ChannelFuture write(Object msg) {
        return write(msg, newPromise());
    }
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }

        if (!validatePromise(promise, true)) {
            ReferenceCountUtil.release(msg);
            // cancelled
            return promise;
        }
        write(msg, false, promise);

        return promise;
    }
    private void write(Object msg, boolean flush, ChannelPromise promise) {

        AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeWrite(msg, promise);
            if (flush) {
                next.invokeFlush();
            }
        } else {
            int size = channel.estimatorHandle().size(msg);
            if (size > 0) {
                ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
                // Check for null as it may be set to null if the channel is closed already
                if (buffer != null) {
                    buffer.incrementPendingOutboundBytes(size);
                }
            }
            Runnable task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, msg, size, promise);
            }  else {
                task = WriteTask.newInstance(next, msg, size, promise);
            }
            safeExecute(executor, task, promise, msg);
        }
    }
    private void invokeWrite(Object msg, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler()).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

    // HeadContext
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        unsafe.write(msg, promise);
    }

寫數(shù)據(jù)不會(huì)直接發(fā)送數(shù)據(jù)到服務(wù)端,而是會(huì)緩存起來,直到調(diào)用flush才會(huì)完成數(shù)據(jù)的最終發(fā)送。flush邏輯與寫數(shù)據(jù)一樣,channel到pipeline,tail到head。最終交由unsafe類來完成

channel.flush();
    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        unsafe.flush();
    }

六 總結(jié)

以客戶端建立連接發(fā)送數(shù)據(jù)流程為例(服務(wù)端大部分邏輯相似),總結(jié)Netty的工作流程:

  1. 聲明NioEventLoopGroup(事件循環(huán)組),內(nèi)部會(huì)創(chuàng)建兩倍核數(shù)大小的數(shù)組,并為數(shù)組上的每一個(gè)位置分配NioEventLoop(事件循環(huán)),NioEventLoop內(nèi)包含一個(gè)未啟動(dòng)線程和一個(gè)任務(wù)隊(duì)列
  2. 聲明Bootstrap(引導(dǎo)),指定事件循環(huán)組、遠(yuǎn)程地址、option、attr、channel類型和自定義的ChannelInitializer
  3. 使用Bootstrap發(fā)起建立連接請(qǐng)求
  • 使用反射根據(jù)指定的channel類型創(chuàng)建channel實(shí)例
  • 對(duì)channel實(shí)例進(jìn)行初始化,配置自定義ChannelInitializer到channel管道中,設(shè)置option和attr
  • 從NioEventLoopGroup維護(hù)的數(shù)組中選出NioEventLoop,借助NioEventLoop開始channel的注冊(cè)邏輯
  • 這個(gè)過程會(huì)激活NioEventLoop內(nèi)創(chuàng)建的線程。該線程進(jìn)而啟動(dòng)一個(gè)無限for循環(huán),開始接收多路復(fù)用器上就緒的IO事件,同時(shí)執(zhí)行NioEventLoop內(nèi)隊(duì)列上的任務(wù)
  • 接著channel會(huì)與該NioEventLoop綁定,并將channel注冊(cè)到多路復(fù)用器上
  • channel注冊(cè)完成后,執(zhí)行為channel指定的ChannelInitializer,最終在channel上形成完整的管道配置
  • 再設(shè)置channel為readPending的狀態(tài)
  • 待channel準(zhǔn)備就緒后,使用channel建立與服務(wù)端的連接,發(fā)起建立連接的請(qǐng)求,由NioEventLoop完成
  • 處理服務(wù)端返回的建立請(qǐng)求應(yīng)答,IO事件由NioEventLoop完成
  1. Bootstrap在連接建立完成后返回channel實(shí)例,使用channel實(shí)例完成數(shù)據(jù)的發(fā)送。同時(shí)與channel綁定的NioEventLoop內(nèi)的線程,監(jiān)聽并處理多路復(fù)用器上準(zhǔn)備就緒的IO事件(接收服務(wù)端數(shù)據(jù)應(yīng)答或服務(wù)端主動(dòng)發(fā)起的數(shù)據(jù))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。