Get through the network source code series seven worker group registration NioSocketChannel

Keywords: Netty network github socket

Get through the network source code series seven worker group registration NioSocketChannel

worker group registration NioSocketChannel

First, fill in a relatively easy to understand registration chart. Here, temporarily register all new channels to an event cycle of the same worker group, which is actually registered by load balancing:

The registration is the same as the boss group, but at this time, the parameter is NioSocketChannel type, and one will be selected from the NioEventLoop of the worker group to register:

It should be noted that the Unsafe in it is actually of the type NioSocketChannel.NioSocketChannelUnsafe, which inherits from NioByteUnsafe. At a glance, it is byte related, so it is used to read and write data:

We see two unsafe subclasses. The one above is used by NioServerSocketChannel, and the one below is used by NioSocketChannel. In fact, they are only different in read:

register is the same as the method of the parent class. The same as the previous NioServerSocketChannel registration is to submit the registration task and start the thread. You can see that the thread of the worker group is started:

After that, the worker group's thread performs the registration task, such as selector registration SocketChannel instance:

Then, when we do pipeline. Invokehandleraddedifneed(); the handlerAdded method of our custom childHandler will be executed, and the initChannel method of ChannelInitializer will be executed

Then the corresponding handlerAdded, channelRegistered, channelActive methods are started. That is, the processor is added, registered, and activated.
After the task is completed, it enters the select block.

Read message

Once the established connection is established and the message is sent, it will be processed. As before, a series of methods such as processSelectedKeys will be called. Until the read method, the main method is the doReadBytes(byteBuf) method:

  public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();//Byte buffer allocator
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    byteBuf = allocHandle.allocate(allocator);
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));//Here is the key, read issue
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        if (close) {
                            // There is nothing left to read as we received an EOF.
                            readPending = false;
                        }
                        break;
                    }

                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    pipeline.fireChannelRead(byteBuf);//Deliver read event
                    byteBuf = null;
                } while (allocHandle.continueReading());

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

                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }

doReadBytes

    @Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();//Get allocation processor
        allocHandle.attemptedBytesRead(byteBuf.writableBytes());//Set writable bytes
        return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());//Read the data of the channel and write the byte buffer
    }

writeBytes

Writing data to the buffer is the key:

    @Override
    public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
        ensureWritable(length);
        int writtenBytes = setBytes(writerIndex, in, length);
        if (writtenBytes > 0) {
            writerIndex += writtenBytes;
        }
        return writtenBytes;
    }

setBytes

in.read is that the channel reads the data in the socket buffer from the bottom to the byte buffer:

  @Override
    public final int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
        try {
            return in.read(internalNioBuffer(index, length));
        } catch (ClosedChannelException ignored) {
            return -1;
        }
    }
internalNioBuffer

This method actually returns ByteBuffer, that is to say, the bottom layer encapsulates ByteBuffer:

 @Override
    public final ByteBuffer internalNioBuffer(int index, int length) {
        checkIndex(index, length);
        return _internalNioBuffer(index, length, false);
    }
_internalNioBuffer

As expected:

final ByteBuffer _internalNioBuffer(int index, int length, boolean duplicate) {
        index = idx(index);
        ByteBuffer buffer = duplicate ? newInternalNioBuffer(memory) : internalNioBuffer();
        buffer.limit(index + length).position(index);
        return buffer;
    }

In addition, DirectByteBuffer is a direct buffer, which reduces the data copy from kernel to user space once:

allocHandle.lastBytesRead(doReadBytes(byteBuf))

Then we will make some records. For example, this time we read 11 bytes. Why 11 bytes? Because my client sent a hello world, we will see:

pipeline.fireChannelRead(byteBuf)

Next is to transfer the buffer data to the pipeline and let the processor handle it. As mentioned in the previous article, how to transfer it is not much to say, but what should be noted here is:

If it is a reference counting tired heart, it will be encapsulated:


We can see that all byte buffers customized by netty are of reference count type:

So in the end, the touch(java.lang.Object) of AbstractReferenceCountedByteBuf is executed, seemingly unchanged:

Finally, it is passed to my customized processor and read out:

My client is sent by NIO:

summary

Basically, your process is almost the same as the one in the previous article, but there is a little difference in reading data. Now that we know the general process, we will continue to look at the details later. In fact, I'm talking about the general process, but it's very important to be clear about the skeleton. Otherwise, I'll drill into the details at the beginning, and I won't be able to get out.

Well, I'm here today. I hope it will be helpful for learning and understanding. God can't see it. Just for his own learning and understanding, his ability is limited. Please forgive me.

133 original articles published, 43 praised, 20000 visitors+
Private letter follow

Posted by daredevil14 on Mon, 03 Feb 2020 06:30:44 -0800