Netty source code reading (2) - summary of server source code

Above, we have a general understanding of the source code of the client, so that it is easier to understand the source code of the server. We will analyze the difference between the server and the client.

Table of contents

the difference

 ④

 ①


the difference

 ④

Client: .option(ChannelOption.TCP_NODELAY, true)

In the TCP/IP protocol, no matter how much data is sent, a protocol header is always added in front of the data. At the same time, when the other party receives the data, it also needs to send ACK to indicate confirmation. In order to utilize the network bandwidth as much as possible, TCP always wants to send as much data as possible. This involves an algorithm called Nagle, the purpose of which is to send large blocks of data as much as possible to avoid flooding the network with many small data blocks.
TCP_NODELAY is used to enable or about the Nagle algorithm. If high real-time performance is required, the data will be sent immediately when there is data to be sent. Set this option to true to turn off the Nagle algorithm; if you want to reduce the number of sending times and reduce network interaction, set it to false and wait for a certain size to be accumulated before sending. The default is false.

Server: .option(ChannelOption.SO_BACKLOG, 100)

The server-side TCP kernel module maintains two queues, which we call A and B. When the client connects to the server, the TCP kernel module adds the client connection to the A queue during the second handshake, and then the third time During the handshake, TCP moves the client connection from the A queue to the B queue. After the connection is completed, the application's accept will return, and the accept will take out the connection that has completed the three-way handshake from the B queue.

The sum of the lengths of the A queue and the B queue is the backlog. When the sum of the lengths of the A and B queues is greater than the backlog, the new connection will be rejected by the TCP kernel. Therefore, if the backlog is too small, the accept speed may not be able to keep up. The AB queue is full, causing new clients to be unable to connect. It should be noted that the backlog has no effect on the number of connections supported by the program. The backlog only affects connections that have not been taken out by accept

 Also commonly used: .option(ChannelOption.SO_REUSEADDR, true)

The parameter indicates that the local address and port are allowed to be reused. For example, if a server process occupies TCP port 80 for listening, an error will be returned if the port is listened to again. Using this parameter can solve the problem. This parameter allows sharing the
port , which is more commonly used in server programs,

Client: .channel(NioSocketChannel.class)

Server: .channel(NioServerSocketChannel.class)

How they are loaded and the process of type determination is the same, so let's focus on the difference in instantiation.

Similar to NioSocketChannel, the newSocket method is called, but the next call is different. The server calls the openServerSocketChannel method, while the client calls the openSocketChannel method. As the name implies, one is the Java SocketChannel on the client side, and the other is the Java ServerSocketChannel on the server side.

 Next, the overloaded method is called, and SelectionKey.OP_ACCEPT is passed in here. Friends who have knowledge about Java NIO Socket understand that Java NIO is a Reactor mode, and we use selectors to implement I/O multiplexing. At the beginning, the server needs to listen to the client's connection request, so here we set  SelectionKey.OP_ACCEPT. Do we still have client impressions? Compare the picture below

 Then call the constructor step by step, just like the client code, it will instantiate unsafe and pipeline

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

But newUnsafe is different. The unsafe field on the server side is actually an instance of AbstractNioMessageChannel#AbstractNioUnsafe. And the client is an instance of AbstractNioByteChannel#NioByteUnsafe

 ①

On the client side, we only provided one EventLoopGroup object, while on the server side initialization, we set up two EventLoopGroups, one is bossGroup and the other is workerGroup. So what are these two EventLoopGroups used for? In fact, , the bossGroup is used for the accept of the server, that is, it is used to process the connection request of the client. The workerGroup is actually the one that actually does the work, and they are responsible for the IO operation of the client connection channel. As shown below

The bossGroup on the server side constantly monitors whether there is a client connection. When a new client connection is found, the bossGroup will initialize various resources for this connection, and then select an EventLoop from the workerGroup to bind to this client connection Middle. Then the following interaction process between the server and the client will be all in the assigned EventLoop. Let's follow up the source code analysis.

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup); // 1
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        // 2
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

The code above does two things. 1 is to enter the group method of AbstractBootstrap to specify the group attribute, which is directly here on the client side. 2 is to specify the childGroup attribute of ServerBootstrap. After initializing the properties, where are they used?

bossGroup : AbstractBootstrap.bind -> AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister associates boosGroup with NioServerSocketChannsl

ChannelFuture regFuture = config().group().register(channel);

 workGroup : It is still the init(channel) that appears in the initAndRegister method.

   void init(Channel channel) {
        // 只贴出和childGroup有关的关键代码
        final EventLoopGroup currentChildGroup = childGroup;
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

The init method is rewritten in ServerBootstrap, and childGroup is mainly encapsulated into the ServerBootstrapAcceptor class, so we focus on this class.

We clicked in and saw that it turned out to be a static inner class. The operations related to childGroup are mainly placed in the channelRead method rewritten by the class.

 childGroup.register(child).addListener(...);

The child here is a NioSocketChannel, which means that an EventLoop in the workerGroup is associated with the NioSocketChannel.

So, when is this channelRead method called? When a client connects to the server, the underlying Java NIO ServerSocketChannel will have a  SelectionKey.OP_ACCEPT  ready event, and then it will call NioServerSocketChannel.doReadMessages.

            SocketChannel ch = SocketUtils.accept(javaChannel());
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }

 Obtain the connection to the client through the accept method, and then encapsulate it as a NioSocketChannel. The passed this refers to the NioServerSocketChannel, which is the parent channel that encapsulates the NioSocketChannel. Next, through Netty's ChannelPipeline mechanism, the read event is sent to each handler step by step, so it will trigger the ServerBootstrapAcceptor.channelRead we mentioned earlier.

Like boosGroup and workGroup, handler and childHandler appear here, so will he be the same as the former two? One handles connections and one handles IO events? Let's look at the code.

In ④ we mentioned the rewriting of the init method in ServerBootstrap. It also involves the operation of the handler

        final ChannelHandler currentChildHandler = childHandler; // 1

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler(); //2
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });

First look at 2 (note 2 in the code above), where config.handler() is the handler specified by the server code .handler(new LoggingHandler(LogLevel.INFO)), if it is not empty, add it to the pipeline.

 Then look at 1 (the above code comment 1), the childHandler here is still the property of constructing ServerBootstrapAcceptor, click into this class, and find that this childHandler is in the channelRead method rewritten by ServerBootstrapAcceptor (do you remember when it was called? After the connection is established after)

child.pipeline().addLast(childHandler);

Remember this child, he is a NioSocketChannel, and childHandler is added to his pipeline.

In summary, the following is

  • The handler works in the accept phase, it handles the client's connection request.
  • childHandler works after the client connection is established, and it is responsible for the IO interaction of the client connection.

Guess you like

Origin blog.csdn.net/wai_58934/article/details/127860262