netty (a) --- server source code reading

NIO Select Knowledge

select sample code:

// create a channel and set to a non-blocking 
ServerSocketChannel ServerChannel = ServerSocketChannel.open (); 
serverChannel.configureBlocking (false); 
serverChannel.socket () the bind (new new InetSocketAddress (Port));. 
// Open Selector 
Selector Selector = Selector.open (); 
// Register the Selector 
serverChannel.register (Selector, SelectionKey.OP_ACCEPT); 
// loop through or to, is the main () method to find SelectionKey by select, using the event monitor what type of treatment response 
// where needed Note that the registration method two   
the while (to true) { 
    int n-the selector.select = (); 
    IF (n-== 0) Continue; 
    the Iterator ITE = this.selector.selectedKeys () Iterator ();. 
    the while (ite.hasNext ( )) { 
        the SelectionKey Key = (the SelectionKey) ite.next (); 
        IF (key.isAcceptable ()) {
            = ClntChan the SocketChannel ((a ServerSocketChannel) key.channel ()) Accept ();. 
            ClntChan.configureBlocking (to false); 
            // register selector to the client connected to the channel, 
            and // specifies attributes of the key value for the channel OP_READ, 
            // specify for the associated accessory channel 
            clntChan.register (key.selector (), SelectionKey.OP_READ, ByteBuffer.allocate (bufSize)); 
        } 
        IF (key.isReadable ()) { 
            handleRead (Key); 
        } 
        IF (key.isWritable () && key.isValid ()) { 
            handleWrite (Key); 
        } 
        IF (key.isConnectable ()) { 
            System.out.println ( "to true isConnectable ="); 
        } 
      ite.remove (); 
    }
}

Source Reading

By the previous article we know, netty is actually made up of two Reactor, the former Acceptor maintain a binding interface that handles the connection of the client, and then read and write, decoding and encoding work to another Reactor, let's look what such a process, understand the overall process and then understand the details of the system logic implementation.

Two classes of components:

AbstractEventExecutorGroup

  • MultithreadEventExecuteGroup
    • MultithreadEventLoopGroup
      • NioEventLoopGroup

AbstractChannel

  • AbstractNioChannel
    • AbstractNioByteChannel
      • NioSocketChannel

Important categories:

  • NioServerSocketChannel: server port connecting the channel
  • ChannelPipeline: Handler store chain
  • NioEventLoop: SingleThreadEventLoop implementation which register the Channel's to a Selector and so does the multi-plexing of these in the event loop is responsible for reading the network connection and client requests access Reactor thread is NioEventLoop

We look directly bind method, AbstractBootstrap class

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(String inetHost, int inetPort) {
        return bind(new InetSocketAddress(inetHost, inetPort));
    }


    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }

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

        final ChannelPromise promise;
        if (regFuture.isDone()) {
            promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                void operationComplete public (ChannelFuture Future) throws Exception { 
                    doBind0 (regFuture, Channel, localAddress is, Promise); 
                } 
            }); 
        } 

        return Promise; 
    }         



    / ** 
     * 
     * 
     * 
     * / 
    Final ChannelFuture initAndRegister () { 
        Channel Channel; 
        the try { 
            / / NO.1 Acceptor listening port binding thread waiting for the connection from the client 
            Channel createChannel = (); 
        } the catch (the Throwable T) { 
            return VoidChannel.INSTANCE.newFailedFuture (T); 
        } 

        the try {
            // No.2 附加属性,添加 Handler 
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            return channel.newFailedFuture(t);
        }

        ChannelPromise regFuture = channel.newPromise();
        // No.3 registed 注册连接事件 
        channel.unsafe().register(regFuture);
        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 beause 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;
    }

init method

    @Override
    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            channel.config().setOptions(options);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
        // ChannelPipeline following a list storing the internal list are put handler, p is the parent class ChannelPipeline
        }
            }


        // child following ChannelPipeline is child Reactor, where there will be no judge would have added to the list handler (handle () method we begin with the case), if not 
        // the tail of this method will want to add a ServerBootstrapAcceptor as default list node 
        // that childHandler where the handler to join it? In ServerBootstrapAcceptor, we will introduce follow 
        the ChannelPipeline channel.pipeline P = (); 
        IF (! Handler () = null) { 
            p.addLast (Handler ()); 
        } 

        Final currentChildHandler of ChannelHandler = childHandler; 
        Final the Entry <ChannelOption <>? , Object> [] currentChildOptions; 
        Final the Entry <? AttributeKey <>, Object> [] currentChildAttrs; 
        the synchronized (childOptions) { 
            currentChildOptions = childOptions.entrySet () toArray (newOptionArray (childOptions.size ())).;
        } 
        The synchronized (childAttrs) { 
            currentChildAttrs = childAttrs.entrySet () toArray (newAttrArray (childAttrs.size ()));. 
        } 

        // Note here: ServerBootstrapAcceptor pipleline placed in 
        // performs when receiving a subsequent client request the method of pipleline, specifically in 
        // NioMessageUnsafe read method 
        p.addLast (new new ChannelInitializer <Channel> () { 
            @Override 
            public void initChannel (CH Channel) throws Exception { 
                ch.pipeline (). addLast (new new ServerBootstrapAcceptor ( currentChildHandler, currentChildOptions, 
                        currentChildAttrs)); 
            } 
        }); 
    }

register method

        // is not judged to be self-initiated action NioEventLoop, if there is no concurrency issues directly Channel Register 
        // if it is initiated by another thread, then the package was placed into a message queue Task asynchronously. Because here is where ServerBootstrap 
        // Register the operation thread of execution, all should be packaged as Task delivered to NioEventLoop executed. 
        @Override 
        public void Final Register (ChannelPromise Final Promise) { 
            IF (eventLoop.inEventLoop ()) { 
                register0 (Promise); 
            } {the else 
                the try { 
                    eventLoop.execute (the Runnable new new () { 
                        @Override 
                        public void RUN () { 
                            register0 ( Promise); 
                        } 
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    promise.setFailure(t);
                }
            }
        }


        private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                (! ensureOpen (Promise)) IF { 
                    return; 
                } 
                // Register logic Nos. 1 
                the doRegister (); 
                Registered = to true; 
                promise.setSuccess (); 
                after // No.2 successfully registered, in circulation and TailHandler channelPiple in the HeaderHandler (Note: the internal selectKey registered as 0, not OP_ACCEPT, will modify later) 
                pipeline.fireChannelRegistered (); 
                // channelPiple No. 3 in circulation in the HeaderHandler and TailHandler, HeaderHandler the read method calls 
                // selectKey amended as OP_ACCEPT 
                IF (isActive ()) { 
                    pipeline.fireChannelActive (); 
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                if (!promise.tryFailure(t)) {
                    logger.warn(
                            "Tried to fail the registration promise, but it is complete already. " +
                                    "Swallowing the cause of the registration failure:", t);
                }
            }
        }

initAndRegisted method, we can know from the next major method of operation of the process is to initialize and registration, is above No.1 2 3 is operating on the channel. createChannel method ServerBootstrap this class

    @Override 
    Channel createChannel () { 
        // Next method retrieves a thread to do the work of connecting 
        EventLoop eventLoop = Group () Next ();. 
        Return ChannelFactory () newChannel (eventLoop, childGroup);. 
    }

Method MultithreadEventExecuteGroup next class, group () is returned bossGroup, a method for obtaining its next available thread from the thread group

    @Override
    public EventExecutor next() {
        return children[Math.abs(childIndex.getAndIncrement() % children.length)];
    }

NioServerSocketChannel by createChannel () method of reflection is created, while registering an event connected

    /**
     * Create a new instance
     */
    public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {
        super(null, eventLoop, childGroup, newSocket(), SelectionKey.OP_ACCEPT);
        config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());
    }

Join the server has done the join operation with the client, then the next step should be to the IO operation should reach read and write process (create a new thread for processing) workGroup of Reactor, the server processing NioEventLoop class run the method,

NioEventLoop run method

    @Override
    protected void run() {
        for (;;) {
            oldWakenUp = wakenUp.getAndSet(false);
            try {
                if (hasTasks()) {
                    selectNow();
                } else {
                    select();

                    // 'wakenUp.compareAndSet(false, true)' is always evaluated
                    // before calling 'selector.wakeup()' to reduce the wake-up
                    // overhead. (Selector.wakeup() is an expensive operation.)
                    //
                    // However, there is a race condition in this approach.
                    // The race condition is triggered when 'wakenUp' is set to
                    // true too early.
                    //
                    // 'wakenUp' is set to true too early if:
                    // 1) Selector is waken up between 'wakenUp.set(false)' and
                    //    'selector.select(...)'. (BAD)
                    // 2) Selector is waken up between 'selector.select(...)' and
                    //    'if (wakenUp.get()) { ... }'. (OK)
                    //
                    // In the first case, 'wakenUp' is set to true and the
                    // following 'selector.select(...)' will wake up immediately.
                    // Until 'wakenUp' is set to false again in the next round,
                    // 'wakenUp.compareAndSet(false, true)' will fail, and therefore
                    // any attempt to wake up the Selector will fail, too, causing
                    // the following 'selector.select(...)' call to block
                    // unnecessarily.
                    //
                    // To fix this problem, we wake up the selector again if wakenUp
                    // is true immediately after selector.select(...).
                    // It is inefficient in that it wakes up the selector for both
                    // the first case (BAD - wake-up required) and the second case
                    // (OK - no wake-up required).

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

                cancelledKeys = 0;

                final long ioStartTime = System.nanoTime();
                needsToSelectAgain = false;
                if (selectedKeys != null) {
                    processSelectedKeysOptimized(selectedKeys.flip());
                } else {
                    processSelectedKeysPlain(selector.selectedKeys());
                }
                final long ioTime = System.nanoTime() - ioStartTime;

                final int ioRatio = this.ioRatio;
                runAllTasks(ioTime * (100 - ioRatio) / ioRatio);

                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        break;
                    }
                }
            } catch (Throwable t) {
                logger.warn("Unexpected exception in the selector loop.", t);

                // Prevent possible consecutive immediate failures that lead to
                // excessive CPU consumption.
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // Ignore.
                } 
            } 
        } 
    } 

    // Whether or processSelectedKeysPlain processSelectedKeysOptimized method has the following method after 
    // you can see what is listening to is select the method of read and write operations. 
    static void processSelectedKey Private (the SelectionKey K, AbstractNioChannel CH) { 
        Final NioUnsafe the unsafe ch.unsafe = (); 
        IF (! k.isValid ()) { 
            // Close Channel The IF IS Not Valid The Key anymore 
            unsafe.close (the unsafe. voidPromise ()); 
            return; 
        } 

        the try { 
            int k.readyOps the readyOps = (); 
            // Check for Also readOps Possible workaround of 0 to Which the JDK bugs otherwise Lead On May
            // 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 e) {
            unsafe.close(unsafe.voidPromise());
        }
    }

If the event is read, the call unsafe.read () method, will come AbstractNioMessageChannel inner class NioMessageUnsafe read method

public abstract class AbstractNioMessageChannel extends AbstractNioChannel {

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

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

    private final class NioMessageUnsafe extends AbstractNioUnsafe {

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

        private void removeReadOp() {
            SelectionKey key = selectionKey();
            int interestOps = key.interestOps();
            if ((interestOps & readInterestOp) != 0) {
                // only remove readInterestOp if needed
                key.interestOps(interestOps & ~readInterestOp);
            }
        }


        @Override
        public void read() {
            assert eventLoop().inEventLoop();
            if (!config().isAutoRead()) {
                removeReadOp();
            }

            final ChannelConfig config = config();
            final int maxMessagesPerRead = config.getMaxMessagesPerRead();
            final boolean autoRead = config.isAutoRead();
            final ChannelPipeline pipeline = pipeline();
            boolean closed = false;
            Throwable exception = null;
            // 这里有个循环
            try {
                for (;;) {
                    // No.1 
                    int localRead = doReadMessages(readBuf);
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }

                    if (readBuf.size() >= maxMessagesPerRead | !autoRead) {
                        break;
                    }
                }
            } catch (Throwable t) {
                exception = t;
            }

            int size = readBuf.size();
            for (int i = 0; i < size; i ++) {
                // No.2 调用 pipleline里的方法
                pipeline.fireChannelRead(readBuf.get(i));
            }
            readBuf.clear();
            pipeline.fireChannelReadComplete();

            if (exception != null) {
                if (exception instanceof IOException) {
                    // ServerChannel should not be closed even on IOException because it can often continue
                    // accepting incoming connections. (e.g. too many open files)
                    closed = !(AbstractNioMessageChannel.this instanceof ServerChannel);
                }

                pipeline.fireExceptionCaught(exception);
            }

            if (closed) {
                if (isOpen()) {
                    close(voidPromise());
                }
            }
        }
    }

    ...
    ...

And finally to the doReadMessages method of NioServerSocketChannel.

    @Override 
    protected int doReadMessages (List <Object> buf) throws Exception { 
        the SocketChannel CH = javaChannel () Accept ();. 

        The try { 
            IF (CH = null!) { 
                // Open childGroup Reactor in a thread so that read and write operations, and passed into the array NioSocketChannel 
                buf.add (new new NioSocketChannel (the this, childEventLoopGroup () Next (), CH).); 
                return. 1; 
            } 
        } the catch (the Throwable T) { 
            logger.warn ( "the Failed to Create AN accepted from new new Channel A Socket ", T);. 

            the try { 
                ch.close (); 
            } the catch (the Throwable T2) { 
                logger.warn (" the Failed to Close Socket A ", T2).;
            }
        }

        return 0;
    }
doReadMessages 执行结束后我们会继续继续执行 No.2 处的代码,执行 pipleline 中放入对象的的方法,又上一篇可以知道将会执行
ServerBootstrapAcceptor的 channnelRead 方法
        @Override 
        @SuppressWarnings ( "an unchecked") 
        public void channelRead (ChannelHandlerContext CTX, Object MSG) { 
            Channel Child = (Channel) MSG; 
            childHandler ChannelPiple added to the client's SocketChannel Nos. 1 starts when // 
            // look back examples of what we started, this is the place to join childHandler 
            child.pipeline () addLast (childHandler);. 
            // set No. 2 SocketChannel client's TCP parameters 
            for (Entry <ChannelOption <>, Object?> e: childOptions) { 
                the try { 
                    IF (child.config () the setOption ((ChannelOption <Object>) e.getKey (), e.getValue ())!.) { 
                        logger.warn ( "Unknown Channel Option:" + E);
                    }
                } catch (Throwable t) {
                    logger.warn("Failed to set a channel option: " + child, t);
                }
            }

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
            // No.3 
            child.unsafe().register(child.newPromise());
        }

Reference material

  • http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html (Reator model)
  • https://www.jianshu.com/p/052035037297
  • https://www.jianshu.com/p/0d497fe5484a

Guess you like

Origin www.cnblogs.com/Benjious/p/11613235.html