Location>code7788 >text

Getting to know the basic components of netty

Popularity:217 ℃/2024-07-30 22:20:17

Java NIO VS Netty

With Java NIO, and the fact that Netty is based on a Java NIO implementation, why can't you just use Java NIO to implement a network communication module? I'll explain why next.

If we use Java NIO to develop network communication components, we are bound to face a lot of network communication problems directly. For example, how to deal with network connection anomalies, how to deal with network flash breaks, network congestion, packet splitting and sticky packets, and a whole lot of other network communication problems. At the same time, it will also face the problem of performance optimization, such as mature middleware in order to improve the communication performance, as well as to improve the processing of requests, will be designed into reactor mode.

So, directly using Java NIO as a communication module, there will be a lot of production environment issues waiting for us to deal with, most of the experience is not very senior students is very difficult to realize.

However, in contrast, Netty has many advantages for developing communication components.

  • First of all, Netty simplifies the Java NIO API, encapsulates the underlying details of many complex network passes, and makes it easy for us to develop programs.
  • Secondly, Netty offers a lot of advanced features that are easy to extend.
  • Most importantly, good Netty design enables high-performance, highly concurrent, high-throughput, and highly reliable network communications.
  • Finally, Netty is used as a network communication module in a large number of commercial projects, such as Dubbo and RocketMQ, and has been proven in many production environments to be the most mature and popular network communication module in Java software.

But Netty also has a disadvantage. Netty creates a lot of abstractions to better encapsulate Java NIO, and these abstractions are not trivial for beginners.

Overall, Netty is indeed more complete and robust than Java NIO, but it's also harder to understand.

To give you a better understanding of Netty, I'll walk you through a simple implementation of Netty'sA network communication demo with server and client.

Demo: Netty Getting Started Program

In this demo program, I will explain in detail the meaning of each step in the program, so that you can get started with Nettty development faster.

Here I will start with theserver-sidecap (a poem)client (computing)These two ends are told separately.

Server-side code

The server-side code includes the server-side startup class and the Handler class to handle network events. The startup class is mainly for initialization of some Netty core classes and port binding; the Handler class is used to handle the business logic corresponding to network events.

First, the server-side startup class NettyServer code is as follows:

 
java
public class NettyServer {

    public static void main(String[] args) {
        // As a first step, create two separate EventLoopGroups that handle the network.
        EventLoopGroup parentGroup = new NioEventLoopGroup();   // Acceptor thread group
        EventLoopGroup childGroup = new NioEventLoopGroup();    //Processor or Handler Thread Groups

        try{
            // Step 2: Initialize the server
            ServerBootstrap serverBootstrap = new ServerBootstrap(); // Equivalent to a Netty server
            // In the third step, make a series of configurations to the server.
            serverBootstrap
                    .group(parentGroup, childGroup)
                    .channel()//ServerSocketChannel listening to the port.
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() { //SocketChannel for each connection, SocketChannel represents each connection.
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ().addLast(new NettyServerHandler()); //Processing logic for web requests
                        }
                    });
            ("Server started").
            // Step 4: Bind the port.
            ChannelFuture channelFuture =  (50099).sync(); //Listen on the specified port
            // Step 5: Wait for the server to shut down
            ().closeFuture().sync();// Synchronized waiting for the results of shutting down the startup server
        }catch (Exception ex){
            ();
        }finally {
            ();
            ();
        }

    }
}
  • The first step is to create two EventLoopGroups, which are used to handle network events and are essentially athreading groupThe first parentGroup can be interpreted asReactor modeinnerAcceptorEventLoopGroup is more complex and will be described in more detail later in this article.

  • The second step is to initialize the server class ServerBootstrap, which means ServerBootstrap represents the server.

  • The third step is to make some necessary configurations for the server class ServerBootstrap, including the two thread groups defined earlier as initialization parameters, and then selecting the NioServerSocketChannel that handles the connections on the server side. finally, and most importantly, configure the class that handles the network events, where we have defined the Here we define NettyServerHandler as the class that handles network events on the SocketChannel. Here, you can also see that the class that handles the SocketChannel can be chained, that is, here's thepipeline(), it's a good design.

  • In the fourth step, the server needs a port to provide services to the outside world, and the port bound here is 50099.

  • Step 5: Wait for the server to shut down.

Let's next look at how the custom class NettyServerHandler, which handles network events, is written.

The NettyServerHandler class inherits the ChannelInboundHandlerAdapter class provided in the Netty class library to implement business operations. In other words, Netty has encapsulated the complex network issues, we just need to focus on data processing. The code for NettyServerHandler, the class that handles network events, is as follows:

 
java
 // This class is very much like the processor thread in the reactor pattern, responsible for reading the zone request and returning the response.
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // The first step is to get the content of the client request
        ByteBuf buffer= (ByteBuf) msg;
        byte[] requestBytes = new byte[()];
        (requestBytes);
        
        String request = new String(requestBytes,"UTF-8");
        ("Request received "+request).
        // Step 2, return information to the client
        String response = "Return the response after receiving the request".
        ByteBuf responseBuffer = (());
        (responseBuffer);
    }

    @Override
    // 
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // Real send
        ();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ();
        ();
    }

    @Override
    // As soon as the channel gets through, it executes the
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ("Server is Active......");
    }
}
  • In the first step, when a client sends a request to this server, the server sends a network read event. This triggers the execution of channelRead(). It first initializes a byte array, then reads the binary data from the request, and finally converts it to Chinese characters.
  • In the second step, the method () is called to send the response data to the client.

However, it should be noted that calling () does not mean that the data has been sent, because the operating system has to send the data according to its own situation. In this case, if consistency is important, we can overload the channelReadComplete() method and call the () method so that the data is sent out synchronously.

Of course, there are other methods such as channelActive(), which indicates that the channel is available even if there is a client connection and the connection is successful.

Combining the two classes above, I'll draw you a server-side flowchart:

Well, the server-side program has been introduced to you, followed by the client-side program.

Client Code

Like server-side programs, client-side programs are divided into a startup class and a Handler class that handles network events.

The server-side startup class NettyClient has the following code:

 
java
public class NettyClient {
    public static void main(String[] args) {
        // The first step is to define an EventLoopGroup.
        EventLoopGroup parent = new NioEventLoopGroup();   // Acceptor thread group
        try{
            Bootstrap bootstrap= new Bootstrap();
            // The second step is to do various configurations on the client
            (parent)
                    .channel()
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<Channel>() {
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
                            ().addLast(new NettyClintHandler());
                        }
                    });
            // Step 3, connect to the server
            ChannelFuture channelFuture= ("127.0.0.1",50099).sync();
            ().closeFuture().sync();
        }catch (Exception ex){
            ();
        }
    }
}
  • The first step is to define an EventLoopGroup. Unlike the server-side startup class, the client-side startup class defines only theAn EventLoopGroup objectThis object is defined to assign connection events to theAcceptor thread. Why this design? Because the client side doesn't have thousands of connections like the server side, and there are few network events. Therefore, there is no need to separate the Acceptor thread from the Processor thread to assign different tasks.

  • The second step is to define a bootstrap class and configure the parameters of the bootstrap, for example, NioSocketChannel for Channel (which is not the same as the one used on the server side), and also customize a NettyClintHandler to handle network events.

  • The third step is to request a connection from the server.

Next, look at how the Handler class, which handles network events on the client side, is written.

The code for the class NettyClientHandler, which handles network events, is as follows:

 
java
public class NettyClintHandler extends ChannelInboundHandlerAdapter {
        // The first step is to define the content to be sent
        private ByteBuf requestBuffer;
        
        public NettyClintHandler(){
            byte[] requestBytes = "Sending Request".getBytes();
            requestBuffer = ();
            (requestBytes);
        }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
            // In the second step, send a message to the server
            (requestBuffer);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // In the third step, read the server-side response
        ByteBuf responseBuffer = (ByteBuf) msg;
        byte[] responseBytes = new byte[()];
        (responseBytes);

        String response = new String(responseBytes,"UTF-8");
        ("Received server-side response: "+response);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ();
        ();
    }
}
  • The first step is to define the request information that the client is going to send to the server.

  • In the second step, when the client's connection request to the server is successful and the channel is connected properly, the method channelActive() is triggered, in which we send the request by calling (requestBuffer).

  • In the third step, after sending the request, the server will send a response to the client, which will trigger the execution of the channelRead() method so that we can read the response.

Here, with Netty to implement the server and client code on the end of the explanation, you can see that Netty to the underlying Java NIO all blocked out, we only need to focus on the configuration parameters, only care about the business implementation of the class can be.

It is recommended that you try to run it locally so that you understand it better.

Design Thinking Learned from Code

  1. Separation of network functions and business logic functions

First of all, you can see that the startup class, which is responsible for the communication module, and the Handler class, which is responsible for handling network events, are separated. The advantage of this is that the business logic functions of the Handler class are separated from the communication functions of the startup class. The benefit of categorizing the functions is obvious.Reduced coupling

  1. chain-of-responsibility design pattern

According to the above code, the handling of network events can be handled by multiple Handler objects, and Netty uses chained calls to link the Handler objects together. In fact, this design is also to reduce the coupling, we can handle network events into several steps, each step by a Handler. On the one hand, we can achieve decoupling, and the Handler classes representing different functions do not affect each other. On the other hand, for a Channel, we canFlexibility to increase or decrease the Hanlder that handles it. This makes it more flexible and convenient.

  1. event-driven

In addition, the event-driven idea is also well reflected in the Handler class, there are many methods that represent events, such as the method channelRead() that represents the read event, and the method channelActive() that represents the Channel connection is active. The advantage of event-driven is thatThe code will be very readable and at the same time relatively easy to understand

summarize

  • First, the scenarios in which Netty is used are explained.
  • Then, it describes some of the problems of developing communication modules for Java NIO and the advantages of developing communication modules for Netty.
  • After that, it focuses on the implementation of server-side and client-side code with Netty, so that you can have a preliminary experience of the use of Netty, but also explains some Netty-related components.
  • Finally, we learned some great design ideas from the code examples, such as, decoupling and event-driven.