1. What is Netty
Netty is a very high-performance JavaNetwork communication framework, used to develop server and client programs, mainly used forHandle TCP/UDP network connections,for example:
Chat Service
Real-time push
High concurrent network communication (such as games, IoT, financial systems)
You can understand Netty as a "network building tool" that is more convenient and more powerful than Java native Sockets. Before we learn more about how Netty works, let’s take a look at the simplest connection between a client and a server in Java.
2. The simplest Java network communication
2.1 What are "client" and "server"?
Let's understand oneA metaphor for real life:Ordering system for milk tea shop
Server (Netty service): milk tea shop (fixed location, waiting for someone else to order)
Client (browser, mobile app, Netty client): Customer (whoever wants to drink milk tea will come)
Communication method (TCP): Telephone (order by phone)
It can be omitted a little more, that is, 💬One person sends a message (client) ➜ Another person receives and responds (server)
2.2 Server
import .*; import .*; public class Server { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8080); // Wait for others to find it on port 8080 ("Server starts, waiting for client connection..."); Socket socket = (); // If someone comes to connect, you will receive it ("The client is connected in"); // Input and output stream: used to read and write data BufferedReader in = new BufferedReader(new InputStreamReader(())); // read PrintWriter out = new PrintWriter((), true); // Write String line; while ((line = ()) != null) { ("Received client message:" + line); ("I received it:" + line); // Back to the client } (); // Close the connection (); } }
2.3 Client
import .*; import .*; public class Client { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 8080); // Connect to the server ("Connecting to the server successfully!"); // Input and output BufferedReader userInput = new BufferedReader(new InputStreamReader()); // Your keyboard input PrintWriter out = new PrintWriter((), true); // Send a message BufferedReader in = new BufferedReader(new InputStreamReader(())); // Receive message String msg; while ((msg = ()) != null) { (msg); // Send to the server String reply = (); //Read the server to return ("The server said: + reply); } (); } }
2.4 Communication between the server and the client
First, the server will start first, and the following will be displayed, and the customer will be told that the port number of my store is 8080.
Server Start,Waiting for client connection...
Then a customer wants to buy something, and then connect to the server store through new Socket("127.0.0.1", 8080); // Connect to the local server, that is, enter the door of the server store 8080. And on the server side, when you see someone connecting through ();, you will receive it and serve it. At this time, the client will output the following
Connecting to the server successfully!
The server will output as follows:
The client is connected
After the client enters: hello through the console, you receive your input through the following code and store it in the userInput variable.
BufferedReader userInput = new BufferedReader(new InputStreamReader());
The client sends a message through the out object
PrintWriter out = new PrintWriter((), true); // Send a message
The client accepts messages through the in object
BufferedReader in = new BufferedReader(new InputStreamReader(())); // Receive news
When msg = ()) != null , that is, when it is detected that the client wants to send a message, the following code is executed:
(msg); // Send to the serverString reply = (); //Read the server to return("The server said: + reply);
After (msg), the information is sent to the server side, and the server side will output the following
Received a client message:hello
At the same time, it passes ("I received:" + line); on the server side, and the client receives the message through reply, and the client will output it
The server says:I've received it:hello
2.5 The relationship between the client and the server is as follows:
Role | effect |
---|
Server | Always waiting for someone else to come (listen to the port) |
Client | Actively initiate connection |
Input/Output | "Channel" for sending and receiving messages |
2. Why do you need a thread model? (Thread Model)
After understanding the basic server-side and client communication, we can continue to deepen and understand some slightly more complex concepts, i.e.Thread。
The simple one in frontServer/ClientIn the example, the server is "serial", which means:
- It is waiting for a client to connect.
- Reply after receiving the message, and then wait for the next connection.
But if you have many clients send messages at the same time,The server will become very slow, because it can only process requests one by one.
So, we need a more efficient way of handling:Concurrent programmingConcurrent programming means being able to handle multiple tasks at the same time.Start the next one without waiting for one task to complete. And each task will not block each other. This isThread poolandEvent loop modelThe value of . In Netty:
- Thread pool: Multiple threads can handle multiple connections simultaneously.
- Event loop model: Each thread (event loop) is only responsible for its own tasks, it willContinuous polling events, such as client connection, data reading, etc.
3. What are "blocking" and "non-blocking"?
❌ Blocking:You go to a restaurant for dinner and the waiter gives you a menu, but you have to wait for them to prepare the dishes before you can eat them. During this period, you can't do anything else.
✅ Non-blocking:After you order the waiter will tell you "wait for a while" and you can do other things. As long as the food is ready, the waiter will tell you, interrupt you to do other things and give you food.
Blocking and non-blocking in TCP communication:
- Blocking: When you initiate a connection or request, the program will wait until the connection is established or data returns.
- Non-blocking: After the request is initiated, the program no longer waits and will continue to perform other tasks. If there is a return result, the program will process the return.
Netty default isNon-blocking, so that it can handle many connections at the same time and will not be blocked by one request.
4. How does Netty handle high concurrency?
Netty by using a threading modelEventLoop (event loop)to handle high concurrency.EventLoopGroup: Manage multiple threads (can be understood as multiple waiters) and is responsible for handling network events.EventLoop: Each thread is responsible for some of its own tasks, such as handling requests from a certain client.
For example:
- A server thread, responsible for monitoring and connecting (waiting for "customers" to enter the store).
- Multiple worker threads, responsible for actual communication (helping customers order orders and cooking).
4.1 The relationship between EventLoop and NIO
Netty usedNIO (non-blocking IO)Model. NIO allows a thread to handle multiple connections. Specifically:
- useSelectorPoll (check) the status of each connection to see if data has arrived.
- useChannelto represent network connection.
- useBufferTo read and write data.
This model allows Netty to face itThousands of concurrent connectionsIt can also be efficient when it is .
In summary, Netty's EventLoopGroup manages multiple threads, each thread only does specific things. Assuming that a thread only does the connection client, Netty introduced the NIO model, so the thread responsible for handling the connection is also asked to be the thread responsible for handling the connection.Ability to handle multiple connection requests at the same time。
5. Actual Netty server examples
public class EchoServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(1); // Responsible for receiving connections EventLoopGroup workerGroup = new NioEventLoopGroup(); // Responsible for handling requests try { ServerBootstrap b = new ServerBootstrap(); (bossGroup, workerGroup) .channel() .childHandler(new EchoServerHandler()); ChannelFuture f = (8080).sync(); // Bind the port and start listening ().closeFuture().sync(); } finally { (); (); } } // Process messages sent by the client public static class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // Write it back to the client after receiving the data ("Received the message:" + msg); (msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { (); (); // An exception occurs to close the connection } } }
- ServerBootstrap: It is the core class in Netty used to start the server and start the Netty server.
- bossGroup and workerGroup: Manage event loops and handle tasks that receive connections and process data separately.
- EchoServerHandler: It is our customized business processing logic, and it will be transmitted back intact when we receive the client's message.
6. Actual Netty
6.1 NettyServer Class
ServerBootstrap: The core class started by Netty server.
ServerBootstrap serverBootstrap = new ServerBootstrap() .group(bossGroup, workGroup) .channel() .childHandler(new ServerChannelInitializer(delimiter, maxFrameLength)) .localAddress(socketAddress) .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true);
- .group(bossGroup, workGroup) Configures listener threads and worker threads.
-
.channel()
: The Channel type of the server is specified here.NioServerSocketChannel
Applicable to NIO (non-blocking IO), which is a way to deal with high concurrency. -
.childHandler(new ServerChannelInitializer(delimiter, maxFrameLength))
: Configure one for each connectionChannelInitializer
, will be called when each connection is initialized (when each client connection).ServerChannelInitializer
It is a custom initialization class that configures how to process data encoding, business logic, etc. -
.localAddress(socketAddress)
: Configure the local address and port of the binding -
.option(ChannelOption.SO_BACKLOG, 1024)
: Configure the connection queue size on the server side. The maximum queue length is set to 1024. -
.childOption(ChannelOption.SO_KEEPALIVE, true)
: Set TCP KeepAlive to ensure that the connection remains alive while it is idle.
6.1.1 Start and bind ports
ChannelFuture channelFuture = (socketAddress).sync();
.bind(socketAddress)
: Bind to the specifiedsocketAddress
, start listening to the client's connection..sync()
: Blocking method will not continue to execute until the port binding is successful and started.ChannelFuture
Used to get the result of the current operation (whether it is successfully bound)
6.2 SeverChannelInitializer class
In the NettyServer class, we call the SeverChannelInitializer class. We use the SeverChannelInitializer class to configure how to process data encoding, business logic, etc.When each client connects in, configure its channel's "pipeline" - that is, how to deal with it in what order when this connection receives/sends data.It can be understood as the "assembly instruction manual" of the factory production line.
package com....nettyService; import ; import ; import ; public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> { private String DELIMITER; private int MAXFRAMELENGTH; public ServerChannelInitializer(String delimiter, int maxFrameLength) { DELIMITER = delimiter; MAXFRAMELENGTH = maxFrameLength; } @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ().addLast("logging", new LoggingHandler("DEBUG")); ().addLast("decoder", new HL7Decoder()); ().addLast("encoder", new HL7Encoder()); ().addLast(new NettyServerHandler()); } }
SeverChannelInitializer first inherits ChannelInitializer<SocketChannel>, so that Netty will call it when there is no new connection.initChannel()
Method: Install a set of "processor combination" (pipeline) for this connection.
When this set of "processor combination" receives the message sent by the client, the execution order is as follows:
【Client】==> socketChannel
↓
[LoggingHandler] (Print log)
↓
[HL7Decoder] (Decode message)
↓
[NettyServerHandler] (Business Processing)
When the server wants to reply to a message, its execution order is as follows:
()
↓
[HL7Encoder] (encoded as bytes)
↓
[LoggingHandler] (Print)
↓
【Client】
6.3 NettySeverHandler class
In the SeverChannelInitializer class, it writes the business processing order. When processing the business, the core of its processing business is implemented by the NettySeverHandler class.
package com.....nettyService; import com...; import com....BS2800MPacketParse; import ; import ; import ; import .; import org.; import org.; import ; import ; import ; import ; import ; @Component public class NettyServerHandler extends SimpleChannelInboundHandler<String> { private static final Logger logger = (); private BS2800MPacketParse bs2800MPacketParse = (); /** * Load all client channel groups */ private static final Map<String, Channel> ipChannelMap = new HashMap<>(); /** * When the client connects, it will trigger */ @Override public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception { Channel channel = (); (().toString(), channel); ("Client Connection:" + channelHandlerContext); } /** * The client sends a message and will trigger it */ @Override public void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception { Channel channel = (); ("The server received the client message"); // ("The client address of the message sent: " + ()); ("Message sent by the client sending the message:" + msg); String result = msg; String msa = handleParams(channelHandlerContext, result); if ((msa)) { (msa); } } @Override public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception { (channelHandlerContext); } @Override public void channelInactive(ChannelHandlerContext channelHandlerContext) throws Exception { Channel channel = (); // When the channel becomes inactive (disconnected), remove it from ChannelGroup String ip = ().toString(); if ((ip)) { (ip); if (!() || channel == null) { (); } } ("Client address is:" + ip + "The connection has been disconnected"); } /** * An exception trigger occurred */ @Override public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception { (()); } /** * Processing received message messages */ public String handleParams(ChannelHandlerContext channelHandlerContext, String msg) { String msa = null; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); Channel channel = (); if (().toString().contains("10.10.51.213")) { if ((msg)) { String result[] = ("\r"); if ((result) && > 0) { String msh = null; for (String string : result) { if (("MSH")) { msh = string; } } if (("ORU^R01")) { Date date = new Date(); String temp[] = ("\\|", -1); if ((temp) && > 9) { msa = "MSH|^~\\&|||||" + (date) + "||ACK^R01|" + temp[9] + "|P|2.3.1||||0||ASCII|||"; String str = "MSA|AA|" + temp[9] + "|Message accepted|||0|"; msa = msa + "\r" + str; Map<String, String> paramMap = new HashMap<>(); (temp[9], msg); (msg); return msa; } } } } } return msa; } }
6.3.1 Inheritance
NettyServerHandler inherits SimpleChannelInboundHandler<String> Every time a client message is received (alreadyString
Type, indicating that the decoder has completed decoding), it will triggerchannelRead0()
method. We can process logic, save data, make reply, etc.
6.3.2channelActive
Netty will automatically call this method when a client connects in. Save the client's channel toipChannelMap
, it is convenient to use IP to find the connection later. Print client connection information at the same time.
6.3.3channelRead0
Whenever the client sends a message, it will be automatically executed! Get the current Channel (corresponding client)
Channel channel = ();
Print logs to facilitate debugging and see received data
This is the end of this article about the implementation example of using Netty in SpringBoot. For more related content on using Netty, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!