Source code analysis Dubbo Network Communication Chapter NettyServer, HeaderExchangeServer

This article mainly analyzes the implementation details of NettyServer and HeaderExchangeServer.

1,NettyServer

The whole class diagram of NettyServer is as follows: First, take a look at the properties held by the NettyServer object in general:

  • AbstractPeer
    1. private final ChannelHandler handler Event handling Handler.
    2. private volatile URL url The URL of the first service provider for the protocol, The Server only needs the parameters in the URL, which has nothing to do with a specific service.
  • AbstractEndpoint
    1. private Codec2 codec Codec.
    2. private int timeout Timeout time
    3. private int connectTimeout Connection timeout
  • AbstractServer
    1. private InetSocketAddress localAddress: url host:port address.
    2. private InetSocketAddress bindAddress: if it is a multi network card, and it specifies bind.ip and bind.port. If it is empty, it is the same as the localAddress.
    3. private int accepts: abstractserver? Accepts is not used.
    4. Private int idletimeout = 600; abstractserver? Accept is not used.
  • NettyServer
    1. Private map < string, channel > channels: < IP: port, channel > all channels.
    2. private ServerBootstrap bootstrap: the initiator of netty server.
    3. private io.netty.channel.Channel channel: the server listens to the channel.
    4. Private eventloopgroup boss group; netty boss thread group (responsible for connection events)
    5. private EventLoopGroup workerGroup: network work thread group (responsible for IO events)

1.1 construction method of nettyserver

public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}

Directly call the public abstractserver (URL, ChannelHandler handler) method of the parent class. From the previous article, we know that ChannelHandlers.wrap method will encapsulate channelhandler, mainly adding event distribution mode (Dispatch).

1.1.1 AbstractServer construction method
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);       // @1
        localAddress = getUrl().toInetSocketAddress();   // @2

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
            bindIp = NetUtils.ANYHOST;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);   // @3
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);  
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT); // @4
        try {
            doOpen();   // @5
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        //fixme replace this with better method
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

Code @ 1: call the construction method of the parent class, mainly initializing AbstractPeer (channelHandler, url) and AbstractEndpoint (codec2, timeout, idleTimeout)

Code @ 2: create a localAddress based on the host and port in the URL.

Code @ 3: if < Dubbo: parameter key = "bind. IP" value = "" / > and < Dubbo: parameter key = "bind. Port" / > are configured, then use the IP and port to create bindAddress, which is usually used for multiple network cards. If not configured, the binding IP and port of bindAddress and localAddress are the same.

Code @ 4: initialize accept and idleTimeout. These two parameters are not used elsewhere.

Code @5 , call the doOpen method, and formally establish a network listener on the corresponding port.

1.2 source code analysis

protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        bootstrap = new ServerBootstrap();       // @1
        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));    // @2
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                new DefaultThreadFactory("NettyServerWorker", true));    // @3
        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);   // @4
        channels = nettyServerHandler.getChannels();
        bootstrap.group(bossGroup, workerGroup)                                                                        // @5
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<niosocketchannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug                       
                                .addLast("decoder", adapter.getDecoder())   
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());     // @6
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();
    }

Code @ 1: create the Netty server startup help class ServerBootstrap

Code @ 2: create a server-side Boss thread, thread Name:. NettyServerBoss, which is mainly responsible for the connection events of the client and the main thread (connection events) in the multi Reactor thread model.

Code @ 3: create a server-side Work thread group. Thread Name: NettyServerWorker - sequence number. The number of threads is taken from the parameter: iothreads. The default value is (CPU core number + 1) and 32. As the name implies, IO thread number mainly deals with read-write events. Encoding and decoding are completed in io thread.

Code @ 4: create user Handler, here is NettyServerHandler. Code @ 5: the normal writing method of Netty startup. Pay attention to the following:

 addLast("decoder", adapter.getDecoder()): add decoder
 addLast("encoder", adapter.getEncoder()): add encoder
 addLast("handler", nettyServerHandler): add a business Handler.

Here is a brief introduction to the process:

  1. When the client establishes a connection with the server, the connection event of the Boss thread is triggered, the TCP connection is established, and the read event of the channel (Channel0) is registered with the IO thread.
  2. After the client sends the request message to the server, the read event in the IO thread is triggered. First, the adapter.getDecoder() is called to decode a complete request object from the binary stream according to the corresponding request protocol (such as dubbo), and then it is passed to the business handler, such as nettyServerHandler, to execute the corresponding event method, such as receive method.
  3. When the server writes the response results to the Channel, the encoder will first encode the binary stream according to the protocol for the client to decode.

If you want to learn more about Netty, please go to the author's Source code analysis Netty series

2,HeaderExchangeServer

According to the initialization process of Dubbo service end, we can see that in order to encapsulate various network implementation clients (netty, mina), Dubbo introduces the layers of exchange, and there is exchange Server, which implements the Server and holds the specific Server implementation end internally, such as NettyServer.

Next, let's focus on the HeaderExchangeServer The core attributes are as follows:

  • ScheduledExecutorService scheduled: number of heartbeat threads, thread name prefix, Dubbo remoting server heartbeat thread sequence number
  • private final Server server: specific Server implementation class, such as NettyServer.
  • Private scheduledfuture <? > heartbeat timer: heartbeat scheduledfuture can be used to cancel heartbeat and other actions.
  • private int heartbeat: heartbeat interval
  • Private int heartbeat timeout: heartbeat timeout, at least twice of heartbeat

2.1 constructor

public HeaderExchangeServer(Server server) {
        if (server == null) {
            throw new IllegalArgumentException("server == null");
        }
        this.server = server;
        this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
        this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
        if (heartbeatTimeout &lt; heartbeat * 2) {
            throw new IllegalStateException("heartbeatTimeout &lt; heartbeatInterval * 2");
        }
        startHeartbeatTimer();
    }

Note: the heartbeat interval is mainly set by heartbeat parameter. If it is not configured, heartbeat detection will not be started. From the above view, the headerexchange Server holds the Server internally and encapsulates the heartbeat function, which will not be analyzed in detail here.

The author introduces: Ding Wei, the author of "RocketMQ technology insider", RocketMQ community outstanding evangelist, CSDN2019 blogger TOP10, maintains the official account: Middleware interest circle At present, source code analysis Java collection, Java Concurrent contract (JUC), Netty, Mycat, Dubbo, RocketMQ, Mybatis and other source code columns have been published successively. You can click the link to join Middleware knowledge planet , discuss high concurrent and distributed service architecture and exchange source code together.

</niosocketchannel>

Tags: Programming Netty Dubbo network codec

Posted on Sun, 22 Mar 2020 12:16:06 -0400 by b-real