netty summarizes the startup process of the server

Keywords: Programming Netty JDK socket Java

Mainly paste code

Make a summary for yourself (connection server initialization and processing):

1. NioEventLoop is used to positively handle io connection

2.NioEventLoopGroup can be simply understood as two processing groups, one is accepting connection, the other is processing connection, and the chooser in it is NioEventLoop array

Server initialization process

entrance
ChannelFuture f = b.bind(8888).sync();

   

public ChannelFuture bind(int inetPort) {
        return this.bind(new InetSocketAddress(inetPort));
    }
    
    public ChannelFuture bind(SocketAddress localAddress) {
        this.validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        } else {
            return this.doBind(localAddress);
        }
    }


    private ChannelFuture doBind(final SocketAddress localAddress) {
        //Register on initialization
        final ChannelFuture regFuture = this.initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        } else if (regFuture.isDone()) {
            ChannelPromise promise = channel.newPromise();
            //binding
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        promise.registered();
                        AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
                    }

                }
            });
            return promise;
        }
    }
    //Registration and initialization
    final ChannelFuture initAndRegister() {
        Channel channel = null;

        try {
            //The channelFactory here is the object instantiated by reflection passed from the b.channel(NioServerSocketChannel.class) of the startup code
            channel = this.channelFactory.newChannel();
            //initialization
            this.init(channel);
        } catch (Throwable var3) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
            }

            return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
        }

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

        return regFuture;
    }    
    
    //Set ChannelOptions ChannelAttrs to configure the related properties of the server Channel
    //Set ChildOptions ChildAttrs to configure the related properties of each newly connected Channel
    //Config handler configure the server pipeline
    //Add serverbootstrappacceptor add a connector to process the new connection accepted by accpet, and add a nio thread
    void init(Channel channel) throws Exception {
        Map<ChannelOption<?>, Object> options = this.options0();
        synchronized(options) {
            setChannelOptions(channel, options, logger);
        }

        Map<AttributeKey<?>, Object> attrs = this.attrs0();
        synchronized(attrs) {
            Iterator var5 = attrs.entrySet().iterator();

            while(true) {
                if (!var5.hasNext()) {
                    break;
                }

                Entry<AttributeKey<?>, Object> e = (Entry)var5.next();
                AttributeKey<Object> key = (AttributeKey)e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();
        final EventLoopGroup currentChildGroup = this.childGroup;
        final ChannelHandler currentChildHandler = this.childHandler;
        Map var9 = this.childOptions;
        final Entry[] currentChildOptions;
        synchronized(this.childOptions) {
            currentChildOptions = (Entry[])this.childOptions.entrySet().toArray(newOptionArray(this.childOptions.size()));
        }

        var9 = this.childAttrs;
        final Entry[] currentChildAttrs;
        synchronized(this.childAttrs) {
            currentChildAttrs = (Entry[])this.childAttrs.entrySet().toArray(newAttrArray(this.childAttrs.size()));
        }

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

                ch.eventLoop().execute(new Runnable() {
                    public void run() {
                        //Here's a discussion on generating and processing NioEventLoop when there is a new connection
                        pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
                    }
                });
            }
        }});
    }

Next register selector
   

public ChannelFuture register(Channel channel) {
        return this.next().register(channel);
    }
    public EventLoop next() {
        return (EventLoop)super.next();
    }
    public EventExecutor next() {
        return this.chooser.next();
    }


From the above code, we can see the register method SingleThreadEventLoop of nioeventloop (parent class) actually called
   

 public ChannelFuture register(Channel channel) {
        return this.register((ChannelPromise)(new DefaultChannelPromise(channel, this)));
    }

    public ChannelFuture register(ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

The final call is io.netty.channel.AbstractChannel.AbstractUnsafe
   

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
        if (eventLoop == null) {
            throw new NullPointerException("eventLoop");
        } else if (AbstractChannel.this.isRegistered()) {
            promise.setFailure(new IllegalStateException("registered to an event loop already"));
        } else if (!AbstractChannel.this.isCompatible(eventLoop)) {
            promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
        } else {
            //Assignment binds NioEventLoop to channel
            AbstractChannel.this.eventLoop = eventLoop;
            if (eventLoop.inEventLoop()) {
                //Registration process
                this.register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        public void run() {
                            AbstractUnsafe.this.register0(promise);
                        }
                    });
                } catch (Throwable var4) {
                    AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
                    this.closeForcibly();
                    AbstractChannel.this.closeFuture.setClosed();
                    this.safeSetFailure(promise, var4);
                }
            }

        }
    }    

    private void register0(ChannelPromise promise) {
        try {
            if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
                return;
            }

            boolean firstRegistration = this.neverRegistered;
            //here
            AbstractChannel.this.doRegister();
            this.neverRegistered = false;
            AbstractChannel.this.registered = true;
            //The reason for executing this method is that when the pipeline is called, if the pipeline has not been registered on the eventLoop, put the context wrapped by the handler into the variable pendingHandlerCallbackHead, and then execute it here
            AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
            this.safeSetSuccess(promise);
            AbstractChannel.this.pipeline.fireChannelRegistered();
            if (AbstractChannel.this.isActive()) {
                if (firstRegistration) {
                    AbstractChannel.this.pipeline.fireChannelActive();
                } else if (AbstractChannel.this.config().isAutoRead()) {
                    this.beginRead();
                }
            }
        } catch (Throwable var3) {
            this.closeForcibly();
            AbstractChannel.this.closeFuture.setClosed();
            this.safeSetFailure(promise, var3);
        }

    }
    
    
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                //Register channel to jdk on selector on eventLoop
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }


    
    
Start binding
At this time, the NioEventLoop processed by the server is already running channel.eventLoop().execute() has triggered the run method
   

private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
        channel.eventLoop().execute(new Runnable() {
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }

            }
        });
    }

    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        } else {
            boolean inEventLoop = this.inEventLoop();
            if (inEventLoop) {
                this.addTask(task);
            } else {
                //New thread
                this.startThread();
                this.addTask(task);
                if (this.isShutdown() && this.removeTask(task)) {
                    reject();
                }
            }

            if (!this.addTaskWakesUp && this.wakesUpForTask(task)) {
                this.wakeup(inEventLoop);
            }

        }
    }

    private void startThread() {
        if (this.state == 1 && STATE_UPDATER.compareAndSet(this, 1, 2)) {
            try {
                this.doStartThread();
            } catch (Throwable var2) {
                STATE_UPDATER.set(this, 1);
                PlatformDependent.throwException(var2);
            }
        }

    }    
    
    private void doStartThread() {
        assert this.thread == null;

        this.executor.execute(new Runnable() {
            public void run() {
                SingleThreadEventExecutor.this.thread = Thread.currentThread();
                if (SingleThreadEventExecutor.this.interrupted) {
                    SingleThreadEventExecutor.this.thread.interrupt();
                }

                boolean success = false;
                SingleThreadEventExecutor.this.updateLastExecutionTime();
                boolean var112 = false;

                int oldState;
                label1685: {
                    try {
                        var112 = true;
                        //This step actually calls the run method of NioEventLoop
                        SingleThreadEventExecutor.this.run();
                        success = true;
                        var112 = false;
                        break label1685;
                    } catch (Throwable var119) {
                        SingleThreadEventExecutor.logger.warn("Unexpected exception from an event executor: ", var119);
                        var112 = false;
                    } finally {
                        if (var112) {
                            int oldStatex;
                            do {
                                oldStatex = SingleThreadEventExecutor.this.state;
                            } while(oldStatex < 3 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldStatex, 3));

                            if (success && SingleThreadEventExecutor.this.gracefulShutdownStartTime == 0L) {
                                SingleThreadEventExecutor.logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called before run() implementation terminates.");
                            }

                            try {
                                while(!SingleThreadEventExecutor.this.confirmShutdown()) {
                                    ;
                                }
                            } finally {
                                try {
                                    SingleThreadEventExecutor.this.cleanup();
                                } finally {
                                    SingleThreadEventExecutor.STATE_UPDATER.set(SingleThreadEventExecutor.this, 5);
                                    SingleThreadEventExecutor.this.threadLock.release();
                                    if (!SingleThreadEventExecutor.this.taskQueue.isEmpty()) {
                                        SingleThreadEventExecutor.logger.warn("An event executor terminated with non-empty task queue (" + SingleThreadEventExecutor.this.taskQueue.size() + ')');
                                    }

                                    SingleThreadEventExecutor.this.terminationFuture.setSuccess((Object)null);
                                }
                            }

                        }
                    }

                    do {
                        oldState = SingleThreadEventExecutor.this.state;
                    } while(oldState < 3 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState, 3));

                    if (success && SingleThreadEventExecutor.this.gracefulShutdownStartTime == 0L) {
                        SingleThreadEventExecutor.logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called before run() implementation terminates.");
                    }

                    try {
                        while(!SingleThreadEventExecutor.this.confirmShutdown()) {
                            ;
                        }

                        return;
                    } finally {
                        try {
                            SingleThreadEventExecutor.this.cleanup();
                        } finally {
                            SingleThreadEventExecutor.STATE_UPDATER.set(SingleThreadEventExecutor.this, 5);
                            SingleThreadEventExecutor.this.threadLock.release();
                            if (!SingleThreadEventExecutor.this.taskQueue.isEmpty()) {
                                SingleThreadEventExecutor.logger.warn("An event executor terminated with non-empty task queue (" + SingleThreadEventExecutor.this.taskQueue.size() + ')');
                            }

                            SingleThreadEventExecutor.this.terminationFuture.setSuccess((Object)null);
                        }
                    }
                }

                do {
                    oldState = SingleThreadEventExecutor.this.state;
                } while(oldState < 3 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState, 3));

                if (success && SingleThreadEventExecutor.this.gracefulShutdownStartTime == 0L) {
                    SingleThreadEventExecutor.logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called before run() implementation terminates.");
                }

                try {
                    while(!SingleThreadEventExecutor.this.confirmShutdown()) {
                        ;
                    }
                } finally {
                    try {
                        SingleThreadEventExecutor.this.cleanup();
                    } finally {
                        SingleThreadEventExecutor.STATE_UPDATER.set(SingleThreadEventExecutor.this, 5);
                        SingleThreadEventExecutor.this.threadLock.release();
                        if (!SingleThreadEventExecutor.this.taskQueue.isEmpty()) {
                            SingleThreadEventExecutor.logger.warn("An event executor terminated with non-empty task queue (" + SingleThreadEventExecutor.this.taskQueue.size() + ')');
                        }

                        SingleThreadEventExecutor.this.terminationFuture.setSuccess((Object)null);
                    }
                }

            }
        });
    }

Continue to see binding

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return this.pipeline.bind(localAddress, promise);
    }

   
Next is the internal pipeline call chain
   

public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return this.tail.bind(localAddress, promise);
    }

    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        } else if (this.isNotValidPromise(promise, false)) {
            return promise;
        } else {
            final AbstractChannelHandlerContext next = this.findContextOutbound();
            EventExecutor executor = next.executor();
            if (executor.inEventLoop()) {
                next.invokeBind(localAddress, promise);
            } else {
                safeExecute(executor, new Runnable() {
                    public void run() {
                        next.invokeBind(localAddress, promise);
                    }
                }, promise, (Object)null);
            }

            return promise;
        }
    }
    
    //The final call is unsafe.bind of the head
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        this.unsafe.bind(localAddress, promise);
    }

    public final void bind(SocketAddress localAddress, ChannelPromise promise) {
        this.assertEventLoop();
        if (promise.setUncancellable() && this.ensureOpen(promise)) {
            if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
            }

            boolean wasActive = AbstractChannel.this.isActive();

            try {
                AbstractChannel.this.doBind(localAddress);
            } catch (Throwable var5) {
                this.safeSetFailure(promise, var5);
                this.closeIfClosed();
                return;
            }

            if (!wasActive && AbstractChannel.this.isActive()) {
                this.invokeLater(new Runnable() {
                    public void run() {
                        AbstractChannel.this.pipeline.fireChannelActive();
                    }
                });
            }

            this.safeSetSuccess(promise);
        }
    }
    
    //binding
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            this.javaChannel().bind(localAddress, this.config.getBacklog());
        } else {
            this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
        }

    }    

    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return this.pipeline.bind(localAddress, promise);
    }


    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return this.tail.bind(localAddress, promise);
    }
    //HeadContext
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        this.unsafe.bind(localAddress, promise);
    }

    public final void bind(SocketAddress localAddress, ChannelPromise promise) {
        this.assertEventLoop();
        if (promise.setUncancellable() && this.ensureOpen(promise)) {
            if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
            }

            boolean wasActive = AbstractChannel.this.isActive();

            try {
                //jdk binding
                AbstractChannel.this.doBind(localAddress);
            } catch (Throwable var5) {
                this.safeSetFailure(promise, var5);
                this.closeIfClosed();
                return;
            }

            if (!wasActive && AbstractChannel.this.isActive()) {
                //Note that this is the active state to execute the following process
                this.invokeLater(new Runnable() {
                    public void run() {
                        AbstractChannel.this.pipeline.fireChannelActive();
                    }
                });
            }

            this.safeSetSuccess(promise);
        }
    }
    //Call jdk binding port
    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            this.javaChannel().bind(localAddress, this.config.getBacklog());
        } else {
            this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
        }

    }


Subsequent processing
   

public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(this.head);
        return this;
    }
    //readIfIsAutoRead is also processed in head
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
        this.readIfIsAutoRead();
    }

        private void readIfIsAutoRead() {
            if (DefaultChannelPipeline.this.channel.config().isAutoRead()) {
                DefaultChannelPipeline.this.channel.read();
            }

        }

    public Channel read() {
        this.pipeline.read();
        return this;
    }    
    
    public final ChannelPipeline read() {
        this.tail.read();
        return this;
    }
    //Finally, it goes to the unsafe method in the head. The unsafe of the NioEventLoop of the service monitoring link is NioMessageUnsafe
    public void read(ChannelHandlerContext ctx) {
        this.unsafe.beginRead();
    }    

    public final void beginRead() {
        this.assertEventLoop();
        if (AbstractChannel.this.isActive()) {
            try {
                AbstractChannel.this.doBeginRead();
            } catch (final Exception var2) {
                this.invokeLater(new Runnable() {
                    public void run() {
                        AbstractChannel.this.pipeline.fireExceptionCaught(var2);
                    }
                });
                this.close(this.voidPromise());
            }

        }
    }


Register the selectionkey.op'accept event until the server starts. NioEventLoop is also running for(;;;)
   

protected void doBeginRead() throws Exception {
        SelectionKey selectionKey = this.selectionKey;
        if (selectionKey.isValid()) {
            this.readPending = true;
            int interestOps = selectionKey.interestOps();
            if ((interestOps & this.readInterestOp) == 0) {
                selectionKey.interestOps(interestOps | this.readInterestOp);
            }

        }
    }


Summary: initialize NioEventLoop, which is the encapsulation of infinite listening thread. Initialize channel, bind NioEventLoop, bind port, register selectionkey.op'u accept

New connection access
entrance
Io.netty.channel.nio.nioeventloop ා run infinite loop listening when a link enters, the call chain is as follows
   

protected void run() {
        while(true) {
            while(true) {
                try {
                    switch(this.selectStrategy.calculateStrategy(this.selectNowSupplier, this.hasTasks())) {
                    case -2:
                        continue;
                    case -1:
                        //select operation
                        this.select(this.wakenUp.getAndSet(false));
                        if (this.wakenUp.get()) {
                            this.selector.wakeup();
                        }
                    default:
                        this.cancelledKeys = 0;
                        this.needsToSelectAgain = false;
                        int ioRatio = this.ioRatio;
                        if (ioRatio == 100) {
                            try {
                                //Call this when there is a link
                                this.processSelectedKeys();
                            } finally {
                                this.runAllTasks();
                            }
                        } else {
                            long ioStartTime = System.nanoTime();
                            boolean var13 = false;

                            try {
                                var13 = true;
                                this.processSelectedKeys();
                                var13 = false;
                            } finally {
                                if (var13) {
                                    long ioTime = System.nanoTime() - ioStartTime;
                                    this.runAllTasks(ioTime * (long)(100 - ioRatio) / (long)ioRatio);
                                }
                            }

                            long ioTime = System.nanoTime() - ioStartTime;
                            this.runAllTasks(ioTime * (long)(100 - ioRatio) / (long)ioRatio);
                        }
                    }
                } catch (Throwable var21) {
                    handleLoopException(var21);
                }

                try {
                    if (this.isShuttingDown()) {
                        this.closeAll();
                        if (this.confirmShutdown()) {
                            return;
                        }
                    }
                } catch (Throwable var18) {
                    handleLoopException(var18);
                }
            }
        }
    }

    private void processSelectedKeys() {
        if (this.selectedKeys != null) {
            this.processSelectedKeysOptimized();
        } else {
            this.processSelectedKeysPlain(this.selector.selectedKeys());
        }

    }
    
    private void processSelectedKeysOptimized() {
        for(int i = 0; i < this.selectedKeys.size; ++i) {
            SelectionKey k = this.selectedKeys.keys[i];
            this.selectedKeys.keys[i] = null;
            Object a = k.attachment();
            if (a instanceof AbstractNioChannel) {
                this.processSelectedKey(k, (AbstractNioChannel)a);
            } else {
                NioTask<SelectableChannel> task = (NioTask)a;
                processSelectedKey(k, task);
            }

            if (this.needsToSelectAgain) {
                this.selectedKeys.reset(i + 1);
                this.selectAgain();
                i = -1;
            }
        }

    }

    //This method
    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            NioEventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable var6) {
                return;
            }

            if (eventLoop == this && eventLoop != null) {
                unsafe.close(unsafe.voidPromise());
            }
        } else {
            try {
                int readyOps = k.readyOps();
                if ((readyOps & 8) != 0) {
                    int ops = k.interestOps();
                    ops &= -9;
                    k.interestOps(ops);
                    unsafe.finishConnect();
                }

                if ((readyOps & 4) != 0) {
                    ch.unsafe().forceFlush();
                }

                if ((readyOps & 17) != 0 || readyOps == 0) {
                    //In fact, the final call is this method
                    unsafe.read();
                }
            } catch (CancelledKeyException var7) {
                unsafe.close(unsafe.voidPromise());
            }

        }
    }


        public void read() {
            assert AbstractNioMessageChannel.this.eventLoop().inEventLoop();

            ChannelConfig config = AbstractNioMessageChannel.this.config();
            ChannelPipeline pipeline = AbstractNioMessageChannel.this.pipeline();
            Handle allocHandle = AbstractNioMessageChannel.this.unsafe().recvBufAllocHandle();
            allocHandle.reset(config);
            boolean closed = false;
            Throwable exception = null;

            try {
                int localRead;
                try {
                    do {
                        localRead = AbstractNioMessageChannel.this.doReadMessages(this.readBuf);
                        if (localRead == 0) {
                            break;
                        }

                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

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

                localRead = this.readBuf.size();

                for(int i = 0; i < localRead; ++i) {
                    AbstractNioMessageChannel.this.readPending = false;
                    //This is the key. Finally, it is a call chain of head - > unsafe - > serverbootstrap acceptor
                    pipeline.fireChannelRead(this.readBuf.get(i));
                }

                this.readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();
                if (exception != null) {
                    closed = AbstractNioMessageChannel.this.closeOnReadError(exception);
                    pipeline.fireExceptionCaught(exception);
                }

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

            }

        }


Accept link accept add to buf list
   

protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(this.javaChannel());

        try {
            if (ch != null) {
                //this is the server channel, that is, the readinterestopo in the client channel ch created by NioServerSocketChannel ch for jdk is selectionkey.op'u read
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable var6) {
            logger.warn("Failed to create a new channel from an accepted socket.", var6);

            try {
                ch.close();
            } catch (Throwable var5) {
                logger.warn("Failed to close a socket.", var5);
            }
        }


ServerBootstrapAcceptor 

        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;
            //Add business handler
            //The process is as follows: io. Netty. Channel. Defaultchannelpipeline ා addlast (IO. Netty. Channel. Channelhandler...)
            //io.netty.channel.DefaultChannelPipeline#addLast(io.netty.util.concurrent.EventExecutorGroup, io.netty.channel.ChannelHandler...)
            //io.netty.channel.DefaultChannelPipeline#addLast(io.netty.util.concurrent.EventExecutorGroup, java.lang.String, io.netty.channel.ChannelHandler)
            //io.netty.channel.DefaultChannelPipeline#callHandlerAdded0
            //io.netty.channel.AbstractChannelHandlerContext#callHandlerAdded
            // handler().handlerAdded(this); the handlerAdded method in the custom ChannelInitializer of business addition is actually to call the initChannel method, which is the common way we add handle
            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);
            setAttributes(child, childAttrs);

            try {
                //The register process is similar to the channel monitored by the server
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

//workerGroup
public ChannelFuture register(Channel channel) {
    return this.next().register(channel);
}

public ChannelFuture register(Channel channel, ChannelPromise promise) {
    if(channel == null) {
        throw new NullPointerException("channel");
    } else if(promise == null) {
        throw new NullPointerException("promise");
    } else {
        //unsafe here is NioSocketChannel$NioSocketChannelUnsafe
        channel.unsafe().register(this, promise);
        return promise;
    }
}

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if(eventLoop == null) {
        throw new NullPointerException("eventLoop");
    } else if(AbstractChannel.this.isRegistered()) {
        promise.setFailure(new IllegalStateException("registered to an event loop already"));
    } else if(!AbstractChannel.this.isCompatible(eventLoop)) {
        promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
    } else {
        AbstractChannel.this.eventLoop = eventLoop;
        // Start real asynchrony. At this time, the thread is the boss thread instead of the word thread. It will asynchronously start the run method of NioEventLoop at the same time
        if(eventLoop.inEventLoop()) {
            this.register0(promise);
        } else {
            try {
                eventLoop.execute(new OneTimeTask() {
                    public void run() {
                        AbstractUnsafe.this.register0(promise);
                    }
                });
            } catch (Throwable var4) {
                AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
                this.closeForcibly();
                AbstractChannel.this.closeFuture.setClosed();
                this.safeSetFailure(promise, var4);
            }
        }
 
    }
}

//ditto
private void register0(ChannelPromise promise) {
}


protected void doRegister() throws Exception {
    boolean selected = false;
 
    while(true) {
        try {
            this.selectionKey = this.javaChannel().register(this.eventLoop().selector, 0, this);
            return;
        } catch (CancelledKeyException var3) {
            if(selected) {
                throw var3;
            }
 
            this.eventLoop().selectNow();
            selected = true;
        }
    }
}

public ChannelPipeline fireChannelActive() {
    this.head.fireChannelActive();
    if(this.channel.config().isAutoRead()) {
        this.channel.read();
    }
 
    return this;
}


public ChannelHandlerContext read() {
    final AbstractChannelHandlerContext next = this.findContextOutbound();
    EventExecutor executor = next.executor();
    if(executor.inEventLoop()) {
        next.invokeRead();
    } else {
        Runnable task = next.invokeReadTask;
        if(task == null) {
            next.invokeReadTask = task = new Runnable() {
                public void run() {
                    next.invokeRead();
                }
            };
        }
 
        executor.execute(task);
    }
 
    return this;
}

protected void doBeginRead() throws Exception {
    if(!this.inputShutdown) {
        SelectionKey selectionKey = this.selectionKey;
        if(selectionKey.isValid()) {
            this.readPending = true;
            int interestOps = selectionKey.interestOps();
            if((interestOps & this.readInterestOp) == 0) {
                selectionKey.interestOps(interestOps | this.readInterestOp);
            }
 
        }
    }
}

When the client link has a read event, the unsafe.read() method will also be called, but at this time, unsafe is NioByteUnsafe
       

 public final void read() {
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    byteBuf = allocHandle.allocate(allocator);
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        break;
                    }

                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    //This enables the ability to process data
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                } while (allocHandle.continueReading());

                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
              
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }


    

        
Where does netty detect new connections?
The first process executed by the boss thread polls the accpet event, and then the second process creates the connection through the accept method at the bottom of the jdk
How to register to NioEventLoop thread when new connection
In short, the boss thread gets a NioEventLoop by getting the next() method of the chooser. Then register the new connection to the selector on the NioEventLoop.

A NioEventLoop corresponds to a selector

pipeline
Initialize in AbstractChannel

Inbound events
The propagation order in ChannelPipeline is: head - >... - > tail.
Outbound events
The execution order of each ChannelHandler in the ChannelPipeline is: tail - > head.

Posted by KeitaroHimura on Sun, 17 May 2020 07:44:32 -0700