[Netty] Server and Client

Posted by Mythic Fr0st on Sun, 02 Jun 2019 18:13:27 +0200

Welcome to Public Number: [Love Programming]
If there is a need for background reply to the learning materials given 1T in 2019!!

This article is based on Netty 4.1.36 for analysis

Server

The Netty server startup code is basically as follows:

private void start() throws Exception {

        final EchoServerHandler serverHandler = new EchoServerHandler();
        /**
         * NioEventLoop Not a pure I/O thread, it is responsible for reading and writing I/O
         * Two NioEventLoopGroup s were created.
         * They are actually two separate Reactor thread pools.
         * A TCP connection to receive clients,
         * Another is used to handle I/O-related read and write operations, or to perform system Task, timer task Task, and so on.
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //ServerBootstrap is responsible for initializing the netty server and starting listening for port socket requests
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
//                            Add user-defined ChannelHandler for Channel listening for client read/write events
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture f = b.bind().sync();

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully().sync();
            childGroup.shutdownGracefully().sync();
        }
    }

The code in the diagram above can be summarized in the following steps:

1. Create a ServerBootStrap instance
2. Set and bind the Reactor thread pool: EventLoopGroup, EventLoop is the Channel that handles all Selector s registered with this thread
3. Set up and bind the channel on the server side
4, 5. Create ChannelPipeline and handler to handle network events, in which network time flows as streams, and handler accomplishes most of the customizations: such as codec SSl security authentication
6. Bind and start listening ports
7. When the ready channel is rotated, the Reactor thread: NioEventLoop executes the method in pipline, and finally schedules and executes the channelHandler

Service-side Creation Time Series Diagram

ServerBootStrap Boot Start Server

It is the main boot start server, including the following:

  • 1. Create a service-side Channel
  • 2. Initialize the server-side Channel
  • 3. Register Channel with selector
  • 4. Port Binding

1. Create a service-side Channel

Technological process:
First, the bind() from the user code is AbstractBootstrap.bind(), then NioServerSocketChannel, which is passed in by the user through b.channel(NioServerSocketChannel.class), creates the channel by calling the Selector Provider of the underlying jdk, and then creates the corresponding ChannelPipeline.
Refer to the following figure for details and check the source code yourself:

2. Initialize the server-side Channel

The main work is as follows:

1) Set option s cached in NioServerSocketChannelConfig
2) Set attr to channel
3) Save the configured childOptions and configure childAttrs to ServerBootstrapAcceptor
4) Add a ServerBootstrapAcceptor to the pipeline of NioSocketChannel

The main core sources are as follows:

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

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        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 p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

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

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

Summary:
Overall as described in the workflow above.
Specifically recommended: Looking at the ServerBootstrapAcceptor source, you can see that the ServerBootstrapAcceptor adds the childHandler to the end of the childChannel Pipeline when the channelRead event triggers (and when there is a client connection), sets the options and attributes of the childHandler, and finally registers the childHandler with the childGroup

3. Register Channel with selector

The registration process is as follows

Summary:
What the Channel registration process does is associate Channel with the corresponding EventLoop.

1). Each Channel is associated with a specific EventLoop, and all IO operations in this Channel are performed in this EventLoop;

2). When Channel and EventLoop are associated, the register method of the underlying Java NIO SocketChannel will continue to be called to register the underlying Java NIO SocketChannel into the specified selector.

In these two steps, the Netty Channel registration process is completed.

4. Port Binding

The source code flow for port binding is basically as follows. It is better to read the source code for yourself.

Summary:
In fact, the netty port binding is java Channel ().bind (localAddress, config.getBacklog ()) that calls jdk; bind, then TCP link is established successfully, Channel activation events are propagated through channelPipeline.

Client

The general code for client startup is as follows:

  private void start() throws Exception {

        /**
         * Netty The thread pool responsibilities for receiving client requests are as follows.
         * (1)Receives client TCP connections and initializes Channel parameters;
         * (2)Notify ChannelPipeline of link state change events
         */
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host,port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //Bind Port
            ChannelFuture f = b.connect().sync();

            f.channel().closeFuture().sync();
        } catch (Exception e) {
            group.shutdownGracefully().sync();
        }


    }

Technological process:

1. User threads create Bootstrap instances, create client-related parameters through API settings, and initiate client connections asynchronously.
2. Create a Reactor thread group NioEventLoopGroup that handles client connections and I/O reads and writes, defaulting to twice the number of CPU cores.
3. Create a client-side NioSocketChannel using Bootstrap's ChannelFactory and user-specified Channel type, which is similar to the SocketChannel provided by the JDK NIO class library
4. Create a default Channel Handler Pipeline for dispatching and executing network events.
5. Initiate a TCP connection asynchronously to determine if the connection is successful.If successful, register NioSocketChannel directly on the multiplexer to listen for read bits for packet reading and message sending. If not immediately connected, register the connection to listen to the multiplexer and wait for the connection results.
6. Register the corresponding network listening state to the multiplexer.
7. The multiplexer polls a Channel in the I/O field to process the connection results.
8. If the connection is successful, set the Future result, send the connection success event, and trigger the ChannelPipeline execution.
9. The ChannelHandler of the execution system and user is dispatched by ChannelPipeline to execute the logic.

The source call process is as follows:

Summary:
How does a client initiate a TCP connection?

As follows:

Special reminders:
In AbstractChannelHandlerContext.connect()#findContextOutbound this operation returns the result that next is actually the header node, that is, in the next step, next.invokeConnect(), where next is the header node, so it ends up calling HeadContext.connect()

summary

This article mainly describes the simple workflow of netty service and client.
The next time you write about how the server communicates with the client and memory management.

Last

If you're interested in Java, big data, and follow the wave long by QR code, I'll try to bring you value.If you feel you have even a little help, please give me a compliment or forward it.
Focus on Public Number, reply to 2019 with relevant information.

Topics: Java Netty network JDK