The initialization process of ServerBootstrap of Netty source code

Last blog< Netty source code (I) NioEventLoopGroup initialization process >We talked about the initialization process of NioEventLoopGoup. Today, let's talk about the initialization process of ServerBootstrap. We continue to reproduce the code of the previous blog. The specific code is as follows:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;

//Server
public class NettyServer {
    public static void main(String[] args) throws InterruptedException {
        //It is an endless loop, constantly detecting IO events, processing IO events and executing tasks
        //Create a thread group: accept client connections to the main thread
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);//Number of CPUs *
        //Create a thread group: accept network operation worker threads
        EventLoopGroup workerGroup = new NioEventLoopGroup();  //Number of cpu cores * 2

        //It is a startup auxiliary class of the server. It binds the port to start the service by setting a series of parameters
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        // We need two types of people to work, one is the boss and the other is the worker. The boss is responsible for taking over the work from the outside,
        // The received work is assigned to workers. Here, the role of bossGroup is to constantly accept new connections and throw new connections to the worker group for processing
        serverBootstrap.group(bossGroup, workerGroup)
                //Set the implementation of using NioServerSocketChannel as the server channel
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
                .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
                //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
                // Is a core concept of netty, which represents the processor through which data flows
                .handler(new NettyTestHendler())
                //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
                    }
                });
        System.out.println(".........server  init..........");
        // Here is the real startup process. Bind port 9090 and wait for the server to start before entering the downlink code
        ChannelFuture future = serverBootstrap.bind(9090).sync();
        System.out.println(".........server start..........");
        //Wait for the server to close the socket
        future.channel().closeFuture().sync();

        // Close two sets of dead cycles
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

The code we are talking about today is the following part:

//It is a startup auxiliary class of the server. It binds the port to start the service by setting a series of parameters
ServerBootstrap serverBootstrap = new ServerBootstrap();
// We need two types of people to work, one is the boss and the other is the worker. The boss is responsible for taking over the work from the outside,
// The received work is assigned to workers. Here, the role of bossGroup is to constantly accept new connections and throw new connections to the worker group for processing
serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

The above new ServerBootstrap(); The code simply creates a serverbootstrap () class without any events. The specific assignment code is still the following chain call. Let's start with serverbootstrap Group (bossgroup, workergroup) method. Two parameters are passed here. The first parameter is the NioEventLoopGroup that listens to client connections, and the second parameter is the NioEventLoopGroup that handles client events. Let's open the group method and find out. The open code is as follows:

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
  //parentGroup listens to the EventLoopGroup of client connections
  //childGroup the EventLoopGroup that handles client events
  public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    		//First, the group method of the parent class is called. Let's continue to follow
        super.group(parentGroup);
        ObjectUtil.checkNotNull(childGroup, "childGroup");
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }
}

The group method above first calls the group method of the parent class. Let's first open the group method of the parent class. The specific codes are as follows:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
  public B group(EventLoopGroup group) {
        ObjectUtil.checkNotNull(group, "group");
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        // Group of user connection processed = new nioeventloopgroup();
        this.group = group;
        return self();
    }
}

The above code mainly assigns values, and then returns the current object to facilitate the subsequent chain call. At this time, the specific structure of AbstractBootstrap class is shown in the following figure:

The above two figures should be integrated. NioEventLoop is the inherited SingleThreadEventLoop, and SingleThreadEventLoop inherits the SingleThreadEventExecutor, so there is the property of SingleThreadEventExecutor in NioEventLoop, so the final appearance is to move the property in SingleThreadEventExecutor to NioEventLoop, and integrate the two diagrams accordingly to get the appearance of AbstractBootstrap class. After the assignment of AbstractBootstrap class is completed, we continue to check the group method in ServerBootstrap. The specific code is as follows:

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
  //parentGroup listens to the EventLoopGroup of client connections
  //childGroup the EventLoopGroup that handles client events
  public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    		//Method call return of parent class
        super.group(parentGroup);
        ObjectUtil.checkNotNull(childGroup, "childGroup");
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
    		//Subclass assignment. At this time, ServerBootstrap is similar to the above.
        this.childGroup = childGroup;
        return this;
    }
}

At this time, the childGroup in ServerBootstrap performs the corresponding assignment, as shown in the following figure:

It is also a combination of two pictures. Similar to the AbstractBootstrap class. This completes our group method. Let's continue to look at the subsequent methods in the call chain. The specific codes are as follows:

serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

Let's continue to look at the channel(NioServerSocketChannel.class) method. Open the corresponding source code as follows:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
  //Generic type B is serverbootstrap and C is ServerChannel
  public B channel(Class<? extends C> channelClass) {
    //channelClass=NioServerSocketChannel.class
    //First call new reflectivechannelfactory < C > (objectutil. Checknotnull (channelclass, "channelclass")
    return channelFactory(new ReflectiveChannelFactory<C>(
      ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
  }
}

First, we call the method of new reflective channelfactory < C > (objectutil. Checknotnull (channelclass, "channelclass"). We open the corresponding method. The specific code is as follows:

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Constructor<? extends T> constructor;
    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        //Judgment cannot be empty
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            //clazz=NioServerSocketChannel.class
            //constructor=NioServerSocketChannel.class.getConstructor();
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
                    " does not have a public non-arg constructor", e);
        }
    }
}

It can be found that the above code is to assign the nonparametric constructor of the passed class class to the constructor property of the ReflectiveChannelFactory. See the following figure for details:

The properties and methods in reflective channelFactory are as shown in the figure above. After the method is executed, the channelFactory method is directly executed. The specific code is as follows:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
  //Generic type B is serverbootstrap and C is ServerChannel
  public B channel(Class<? extends C> channelClass) {
    //channelClass=NioServerSocketChannel.class
    //First call new reflectivechannelfactory < C > (objectutil. Checknotnull (channelclass, "channelclass")
    return channelFactory(new ReflectiveChannelFactory<C>(
      ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
  }
  public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        //channelFactory=new ReflectiveChannelFactory
        return channelFactory((ChannelFactory<C>) channelFactory);
  }
  public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        ObjectUtil.checkNotNull(channelFactory, "channelFactory");
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }
        //channelFactory=new ReflectiveChannelFactory
        this.channelFactory = channelFactory;
        return self();
   }
}

The last thing is to assign the corresponding value to the channelFactory attribute of the AbstractBootstrap object. At this time, the AbstractBootstrap is shown in the following figure:

At this time, AbstractBootstrap becomes the image above, and finally the channel method is executed. And returned itself. The aspect continues the chain call. We continue to check the subsequent method options of the chain call (channeloption. So_backlog, 128). The specific codes are as follows:

serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

Let's continue to check the option(ChannelOption.SO_BACKLOG, 128) method. Let's click the corresponding source code. The specific source code is as follows:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
	public <T> B option(ChannelOption<T> option, T value) {
        //options=new LinkedHashMap<ChannelOption<?>, Object>();
        ObjectUtil.checkNotNull(option, "option");
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
                options.put(option, value);
            }
        }
        return self();
    }
}

At this time, we can know that if the passed in value is empty, we will directly delete the passed in option from the original options. If the passed in value is not empty, we will put the value into options and finally return the current object for chain call. At this time, AbstractBootstrap looks like the following figure.

The parameters that can be set by options are shown in the following figure:

At this time, the options method is also executed. Finally, it returns itself. Continue the chain call and continue to execute the childOption(ChannelOption.SO_KEEPALIVE, true) method. The specific code is as follows:

serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

At this time, we open the source code of childOption(ChannelOption.SO_KEEPALIVE, true) method, as follows:

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
  public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    //childOptions=new LinkedHashMap<ChannelOption<?>, Object>();
    ObjectUtil.checkNotNull(childOption, "childOption");
    if (value == null) {
      synchronized (childOptions) {
        childOptions.remove(childOption);
      }
    } else {
      synchronized (childOptions) {
        childOptions.put(childOption, value);
      }
    }
    return this;
  }
}
  • It is the same as the options method, except that the objects saved are different. After the above method is executed, the ServerBootstrap class is shown in the following figure:

The parameters that can be set for childOption are the same as those set for option above. At this time, the childOption method returns itself after execution, which is convenient for chain calling. The specific code of the handler method called at this time is as follows:

serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

At this time, execute the handler(new NettyTestHendler()) method. Before looking at the source code, let's look at the NettyTestHendler class. The specific code is as follows:

@ChannelHandler.Sharable
public class NettyTestHendler extends ChannelInboundHandlerAdapter{

    //Channel readiness event
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive-----"+ctx);
    }

    //Read data event
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof NioSocketChannel){
            System.out.println(msg.getClass());
            ctx.fireChannelRead(msg);
            return;
        }
        ByteBuf byteBuf= (ByteBuf) msg;
        System.out.println("channelRead:"+byteBuf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
    }


    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
    }

}

At this time, let's check the source code of the handler method, as shown below:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
	public B handler(ChannelHandler handler) {
        this.handler = ObjectUtil.checkNotNull(handler, "handler");
        return self();
    }
}

The above operation is the same as assignment. Assign the class we have written to the handler attribute in AbstractBootstrap, and then AbstractBootstrap becomes as shown in the following figure:

At this time, the handler method is completed, and the current object is returned, which is convenient to continue the chain call. Let's continue to check the following method childHandler(), and the specific code is as follows:

serverBootstrap.group(bossGroup, workerGroup)
  //Set the implementation of using NioServerSocketChannel as the server channel
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128) //Sets the number of connections waiting in the thread queue
  .childOption(ChannelOption.SO_KEEPALIVE, true)//Keep active connection
  //Indicates the processes that need to go through during server startup. Here, the final top-level interface of NettyTestHandler is ChannelHandler,
  // Is a core concept of netty, which represents the processor through which data flows
  .handler(new NettyTestHendler())
  //It indicates how to deal with a new connection after it comes in, that is, how the boss assigns jobs to the workers
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
      nioSocketChannel.pipeline().addLast(new StringDecoder(), new NettyServerHendler());
    }
  });

At this time, we click the source code of the childHandler() method. The specific source code is as follows:

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
  public ServerBootstrap childHandler(ChannelHandler childHandler) {
        // this.childHandler=new ChannelInitializer<ServerSocketChannel>()
        this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
        return this;
    }
}

Similar to the above handler, they are also assigned values. After assigning values, the ServerBootstrap is shown in the following figure:

At this point, the entire ServerBootstrap class is initialized to the end of the assignment. In the next blog, I will introduce the source code of the process started by the server.

Tags: Netty

Posted by hexguy on Sun, 17 Apr 2022 21:57:08 +0930