This article mainly describes the Netty framework features and important components, hope to have a more intuitive feeling to the Netty framework after reading, hope to help readers quickly get started Netty, reduce some detour.
Mind mapping
! [](https://pic2.zhimg.com/v2-bd1aade8739efcfd403d31dc037da0dd_b.png)
preface
This article mainly describes some features and important components of Netty framework, hope that after reading the Netty framework to have a more intuitive feeling, hope to help readers quickly start Netty, reduce some detments.
Overview of Netty
Official introduction: Netty is an asynchronous event-driven network application framework for rapid development of maintainable high Performance Protocol Servers & Clients. Netty is an asynchronous event-driven network application framework for rapid development of maintainable high-performance protocol servers and clients.
Why use Netty
According to the official website, Netty is a network application framework, development server and client. A framework for network programming. Since it is network programming, Socket will not talk about, why not use NIO?
2.1 the disadvantage of NIO To this issue, I wrote an article before [the NIO introduction] (https://mp.weixin.qq.com/s/GfV9w2B0mbT7PmeBS45xLw) have a comparatively detailed introduction on the NIO, NIO’s main problem is:
-
NIO class library and API complex, high learning cost, you need to master Selector, ServerSocketChannel, SocketChannel, ByteBuffer and so on.
-
Familiarity with Java multithreaded programming is required. This is because NIO programming involves the Reactor model, and you need to be very familiar with multithreading and network programming to write high quality NIO programs.
-
The infamous epoll bug. It causes the Selector to poll for nothing, eventually causing the CPU to be 100%. Until JDK1.7, there was no fundamental solution.
2.2 Advantages of Netty Comparatively, Netty has many advantages:
-
The API is simple to use and cheap to learn.
-
Powerful, built in a variety of decoding encoder, support a variety of protocols.
-
High performance. Netty has the best performance compared to other mainstream NIO frameworks.
-
Active community, found bugs will be timely repair, iteration cycle is short, constantly add new features.
-
Dubbo and Elasticsearch both use Netty, and the quality is proven.
3. Architecture diagram
! [](https://pic3.zhimg.com/v2-8552db7ceabc450d9e0eb8db689155d6_b.png)
The diagram above is the architecture diagram on the homepage of the official website. Let’s analyze it from top to bottom. The green Core modules include zero copy, API libraries, and an extensible event model.
Protocol Support Includes Http, webSocket, Secure Sockets Layer (SSL), Google Protobuf, Zlib /gzip compression and decompression, and Large File Transfer. The red part of Transport Services, including Socket, Datagram, Http Tunnel, etc. The above can be seen Netty function, protocol, transmission mode are more complete, more powerful.
Hello Word forever
First set up a HelloWord project, first familiar with the API, and pave the way for the later study. Based on the following picture:
! [](https://pic4.zhimg.com/v2-7eefba893a65706eb6bbe4115cbd0b83_b.png)
4.1 Introducing Maven dependencies
The version used is 4.1.20, a relatively stable one. Io.net ty netty – all 4.1.20. Final
4.2 Creating a Server Startup Class
Public class MyServer {public static void main(String[] args) throws Exception {// Create two thread groups boosGroup and workerGroup EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap = new ServerBootstrap(); ServerBootstrap = new ServerBootstrap(); // Set two thread groups bootstrap.group(bossGroup, WorkerGroup) / / set the service side channel implementation types. The channel (NioServerSocketChannel. Class) / / set the thread queue to get connection number. The option (ChannelOption SO_BACKLOG, ChildOption (channeloption.so_keepalive, channeloption.so_keepalive, ChildHandler (new ChannelInitializer<SocketChannel>() {@override protected void InitChannel (SocketChannel SocketChannel) throws Exception {// Sets the handler socketchannel.pipeline ().addlast (new) to the pipeline pipeline MyServerHandler()); }}); // Set the system.out.println (" Java enthusiast server ready...") handler for workerGroup EventLoop ); ChannelFuture = bootstrap.bind(6666).sync(); ChannelFuture = bootstrap.bind(6666).sync(); Channelfuture.channel ().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }}}Copy the code
4.3 Creating a Server Processor
/** * A custom Handler must inherit the Netty specified HandlerAdapter * to be associated with the Netty framework. Is similar, the SpringMVC adapter pattern * * / public class MyServerHandler extends ChannelInboundHandlerAdapter {@ Override public void ChannelRead (ChannelHandlerContext CTX, Object MSG) throws Exception {// Obtain the message sent by the client. System.out.println(" received from client "+ ctx.channel().remoteAddress() +" sent message: "+ bytebuf.toString (charsetutil.utf_8)); } @override public void channelReadComplete(ChannelHandlerContext CTX) throws Exception {// Send a message to the client WriteAndFlush (Unpooled. CopiedBuffer (" Has the server received the message and sent you a question mark?" , CharsetUtil.UTF_8)); } @override public void exceptionCaught(ChannelHandlerContext CTX, Throwable Cause) throws Exception {// An Exception occurs, Close channel ctx.close(); }}Copy the code
4.4 Creating a Client Startup Class
public class MyClient { public static void main(String[] args) throws Exception { NioEventLoopGroup eventExecutors = new NioEventLoopGroup(); Try {// Create the bootstrap object and configure the parameter bootstrap bootstrap = new bootstrap (); // Set the thread group bootstrap.group(eventExecutors) // Set the channel implementation type of the client. Channel (nioSocketChannel.class) // Initialize the channel.handler (new) using the anonymous inner class ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { Ch.pipeline ().addLast(new MyClientHandler()); }}); System.out.println(" Client is ready to fly ~"); ChannelFuture = bootstrap.connect("127.0.0.1", 6666).sync(); ChannelFuture = bootstrap.connect("127.0.0.1", 6666). Channelfuture.channel ().closeFuture().sync(); } the finally {/ / close the thread group eventExecutors. ShutdownGracefully (); }}}Copy the code
4.5 Creating a Client Processor
public class MyClientHandler extends ChannelInboundHandlerAdapter { @Override public void ChannelActive (ChannelHandlerContext CTX) throws Exception {// Sends a message to the server Ctx. writeAndFlush(Unpooled. CopiedBuffer (" Kibiba ~ jasmine ~Are you good~ Malaysia ~", charsetutil.utf_8)); } @Override public void channelRead(ChannelHandlerContext ctx, Object MSG) throws Exception {// Receive the message from the server ByteBuf ByteBuf = (ByteBuf) MSG; System.out.println(" received from server "+ ctx.channel().remoteAddress() +" : + bytebuf.toString (charsetutil.utf_8)); }}Copy the code
4.6 test
Start the server first, then the client, and you can see the result:
MyServer prints the result:
! [](https://pic1.zhimg.com/v2-aa144d6ad2688f69b0f5ef7dc916a3f4_b.png)
MyClient prints the result:
! [](https://pic4.zhimg.com/v2-e6bc4dec6eecb3ae30f55c7a6487e1f7_b.png)
5. Netty features and key components
5.1 taskQueue taskQueue
If the Handler Handler has some long-running business processing, it can be sent to taskQueue asynchronously. How to use it, see the code demo:
public class MyServerHandler extends ChannelInboundHandlerAdapter { @Override public void ChannelRead (ChannelHandlerContext CTX, Object MSG) throws Exception { Ctx.channel ().eventloop ().execute(new Runnable() {@override public void run() {try { Thread.sleep(1000); thread.sleep (1000); System.out.println(" long service processing "); } catch (Exception e) { e.printStackTrace(); }}}); }}Copy the code
The taskQueue has a task added to it.
! [](https://pic2.zhimg.com/v2-134204ffd0a90115b9567c793d867db9_b.png)
5.2 scheduleTaskQueue Indicates the queue of delayed tasks
The delayed task queue is very similar to the task queue described above, except that it can be delayed for a certain amount of time, as shown in the code demonstration:
Ctx.channel ().eventloop ().schedule(new Runnable() {@override public void run() {try { Thread.sleep(1000); thread.sleep (1000); System.out.println(" long service processing "); } catch (Exception e) { e.printStackTrace(); } } },5, TimeUnit.SECONDS); // Execute after 5 secondsCopy the code
Still open debug for debugging view, we can have a scheduleTaskQueue task waiting to execute
! [](https://pic4.zhimg.com/v2-8f0eb27afd1cd2e938014afd086ed20f_b.png)
5.3 Future Asynchrony
When setting up the HelloWord project, we see a line like this:
ChannelFuture = bootstrap.connect("127.0.0.1", 6666);Copy the code
Many operations return this ChannelFuture object. What is this ChannelFuture object used for?
ChannelFuture provides an asynchronous way to notify when an operation is complete. In Socket programming, waiting for the result of the response is blocked synchronously. Netty does not block because ChannelFuture fetches the result in an observer-like mode. Take a look at a code demonstration:
/ / add listeners channelFuture. AddListener (new ChannelFutureListener () {/ / use an anonymous inner class, ChannelFutureListener interface // Override operationComplete method @Override public void operationComplete(ChannelFuture Future) throws Exception {// Check whether the operation succeeded if (future.issuccess ()) {system.out.println (" connection succeeded "); } else {system.out.println (" connection failed "); }}});Copy the code
5.4 the Bootstrap and ServerBootStrap
Bootstrap and ServerBootStrap are Netty’s factory classes for creating client and server initiators. Using this factory class makes it very easy to create Bootstrap classes and, according to some of the examples above, can significantly reduce the difficulty of development. Let’s start with a class diagram:
! [](https://pic1.zhimg.com/v2-dab6b780993979fcb86ef14553c16720_b.png)
It can be seen that both classes are derived from AbstractBootStrap abstract classes, so the configuration methods are generally the same.
Generally speaking, the steps for creating an initiator using Bootstrap can be divided into the following steps:
! [](https://pic4.zhimg.com/v2-dd3a866c356ee7bd24d23319d08116ef_b.png)
5.4.1 group ()
In the previous article [the Reactor model] (https://mp.weixin.qq.com/s/vWbbn1qXRFVva8Y9yET18Q), we talked about the server to use two thread group:
-
BossGroup is used to listen for client connections and is responsible for creating connections with clients and registering them with the Selector of the workerGroup.
-
WorkerGroup is used to handle read and write events for each connection.
Creating a thread group is usually done with the following new:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
Copy the code
A little curious, since it is a thread group, what is the default number of threads? In-depth source code:
Private static final int DEFAULT_EVENT_LOOP_THREADS; The static {/ / NettyRuntime availableProcessors () * 2, Twice the number of CPU cores to constant DEFAULT_EVENT_LOOP_THREADS = math.h Max (1, SystemPropertyUtil. Get int (" io.net ty. EventLoopThreads ", NettyRuntime.availableProcessors() * 2)); if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS); } } protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... Args) {// If not passed, use the constant value, i.e., twice the number of CPU cores super(nThreads == 0? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }Copy the code
As you can see from the source code, the default number of threads is twice the number of CPU cores. To imagine a custom thread count, we could use a parameterized constructor:
// Set the number of bossGroup threads to 1 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(16);Copy the code
5.4.2 channel ()
This method is used to set the Channel type, and when the connection is established, the corresponding Channel instance is created based on this setting.
You can see this in debug mode
! [](https://pic4.zhimg.com/v2-727fcdc218f017cd4f4578188ff9f3fb_b.png)
The channel types are as follows:
NioSocketChannel: asynchronous non-blocking client TCP Socket connection.
NioServerSocketChannel: asynchronous, non-blocking, server-side TCP Socket connection.
These two channel types are commonly used because they are asynchronous and non-blocking. So it’s the first choice.
OioSocketChannel: synchronizes blocked CLIENT TCP Socket connections.
OioServerSocketChannel: blocked TCP Socket connections on the server.
Slightly debugged locally, it works a little differently from Nio, blocking, so the API calls are different. Because it is blocking IO, few people will choose to use Oio, so it is hard to find examples. I thought about it a little, after several times of error, finally tune through. The code is as follows:
OioEventLoopGroup bossGroup = new OioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); The bootstrap. Group (bossGroup) / / boosGroup. Only need to set up a thread group channel (OioServerSocketChannel. Class) / / set the server channel implementation type/code/client end, // If OioEventLoopGroup EventLoopGroup eventExecutors = new OioEventLoopGroup(); // If OioEventLoopGroup eventExecutors = new OioEventLoopGroup(); // Set the channel type to OioSocketChannel bootstrap.group(eventExecutors)// Set thread group. channel(oiosocketchannel. class)// Set the channel implementation type for the clientCopy the code
NioSctpChannel: Asynchronous client Stream Control Transmission Protocol (Sctp) connection.
NioSctpServerChannel: Asynchronous Sctp server connection.
Local startup is not successful, online read some netizens comments, it is only in the Linux environment can be started. SCTP is not supported on this platform. Because my computer is Windows system, so the netizen said some truth.
5.4.3 option () and childOption ()
First of all, the difference between the two.
Option () sets the connection that the server uses to receive it, namely the boosGroup thread.
ChildOption () is the connection that the parent pipe receives, that is, the workerGroup thread.
With that in mind, let’s take a look at some common Settings:
The SocketChannel argument, which is the usual argument for childOption() :
SO_RCVBUF Socket specifies the size of the TCP data receiving buffer. TCP_NODELAY TCP parameter: sends data immediately. The default value is true. The SO_KEEPALIVE Socket parameter is used to keep the connection alive. The default value is False. When this function is enabled, TCP proactively detects the validity of idle connections.
ServerSocketChannel option()
The SO_BACKLOG Socket parameter indicates the length of the queue for the server to accept connections. If the queue is full, the client connection will be rejected. The default value is 200 for Windows and 128 for others.
Due to space restrictions, other will not list, we can go online to find information to see, understand.
5.4.4 Setting the Pipeline (key)
ChannelPipeline is Netty’s chain of responsibility for handling requests, and ChannelHandler is the handler for handling requests. Each channel actually has a pipeline of processors.
In Bootstrap, the childHandler() method needs to initialize the channel and instantiate a ChannelInitializer. In this case, you need to override the initChannel() method to initialize the channel. This is where the assembly process takes place. The code is shown as follows:
Bootstrap.childhandler (new ChannelInitializer<SocketChannel>() {@override protected void InitChannel (SocketChannel SocketChannel) throws Exception {// Sets a custom handler for the pipeline pipeline socketChannel.pipeline().addlast (new) MyServerHandler()); }});Copy the code
There are two main types of Handler:
ChannelInboundHandlerAdapter (inbound processor), ChannelOutboundHandler (outbound processor)
Inbound refers to the Channel of data from the underlying Java NIO Channel to Netty.
Outbound refers to the operation of the underlying Java NIO Channel through Netty’s Channel.
ChannelInboundHandlerAdapter processor common events are:
-
Register event fireChannelRegistered.
-
Connection establishment event fireChannelActive.
-
Read events and read completion events fireChannelRead and fireChannelReadComplete.
-
Abnormal notification event fireExceptionCaught.
-
User-defined event fireUserEventTriggered.
-
The Channel can be written fireChannelWritabilityChanged state change events.
-
Connection closing event fireChannelInactive.
ChannelOutboundHandler Common events of the processor are:
-
Bind the port.
-
Connect to server Connect.
-
Write event Write.
-
Flush time Flush.
-
Read event read.
-
Disconnect actively.
-
Close the channel event close.
A similar handler() is used to assemble parent channels, also known as bossGroup threads. In most cases, you don’t use this method.
5.4.5 bind ()
Provides binding server address and port number for server or client, default is asynchronous startup. It is synchronized if the sync() method is added.
There are five overloaded methods with the same name that bind address port numbers. I won’t introduce you one by one.
5.4.6 Gracefully closing the EventLoopGroup
/ / release all resources, including the creation of the thread bossGroup. ShutdownGracefully (); workerGroup.shutdownGracefully();Copy the code
Will close all Child Channels. When closed, the underlying resources are freed.
5.5 the Channel
What is a Channel? Take a look at the official documentation:
A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind
A component connected to a network socket or capable of performing I/O operations such as read, write, connect, and bind.
If the above statement is abstract, here is another:
A channel provides a user: the current state of the channel (e.g. is it open? is it connected?) , the configuration parameters of the channel (e.g. receive buffer size), the I/O operations that the channel supports (e.g. read, write, connect, and bind), and the ChannelPipeline which handles all I/O events and requests associated with the channel.
Translation:
Channel provides users with:
-
The current state of the channel (e.g., is it open? Or connected?
-
Configuration parameters for a channel (such as the size of the receive buffer)
-
The IO operations supported by a channel (such as read, write, connect, and bind) and the Channel pipeline that handles all IO events and requests associated with a channel.
5.5.1 Obtaining the Channel status
boolean isOpen(); // If the channel is open, return true Boolean isRegistered(); // Return true Boolean isActive() if the channel is registered with EventLoop; // Return true Boolean isWritable() if the channel is active and connected; // Returns true if and only if the I/O thread will execute the requested write immediately.Copy the code
That’s how you get the four states of a channel.
5.5.2 Obtaining Channel Configuration Parameters
Get a single configuration message using getOption().
ChannelConfig config = channel.config(); SO_BACKLOG parameter, Integer soBackLogConfig = config.getoption (channeloption.so_backlog); // soBackLogConfig=128 because my launcher is configured with 128Copy the code
Get multiple configurations, use getOptions(), code demo:
ChannelConfig config = channel.config(); Map<ChannelOption<? >, Object> options = config.getOptions(); for (Map.Entry<ChannelOption<? >, Object> entry : options.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } /** SO_REUSEADDR : false WRITE_BUFFER_LOW_WATER_MARK : 32768 WRITE_BUFFER_WATER_MARK : WriteBufferWaterMark(low: 32768, high: 65536) SO_BACKLOG: 128 * /Copy the code
5.5.3 I/O operations supported by channel
Write operations, which demonstrate writing messages from the server to the client:
@Override public void channelRead(ChannelHandlerContext ctx, Object MSG) throws Exception {ctx.channel().writeAndFlush(Unpooled. CopiedBuffer (" This wave, this wave is egg ~", charsetutil.utf_8)); }Copy the code
Client console:
// Received server /127.0.0.1:6666 message: this wave ah, this wave is meat egg chicken ~Copy the code
Connection operation, code demonstration:
ChannelFuture connect = channelFuture.channel().connect(new InetSocketAddress("127.0.0.1", 6666)); // Initiators are usually usedCopy the code
Obtain channel pipeline through channel and do relevant processing:
ChannelPipeline = ctx.channel().pipeline(); // Add the ChannelHandler handler to pipeline. AddLast (new MyServerHandler());Copy the code
5.6 the Selector
In NioEventLoop, the selector has a member variable, this is the selector, nio package before [the nio introduction] (https://mp.weixin.qq.com/s/GfV9w2B0mbT7PmeBS45xLw), I already talked about Selector.
A Netty Selector is the same as a NIO Selector, which is used to listen for events, manage channels registered in the Selector, and implement multiplexers.
! [](https://pic2.zhimg.com/80/v2-f5115d77799c384fa82068946d4d1ea6_720w.png)
5.7 PiPeline and ChannelPipeline
In the previous section, we learned that a Channel can be loaded with a ChannelHandler handler. A Channel cannot have only one ChannelHandler handler. Since there are many ChannelHandlers working on a pipeline, there must be a sequence.
Thus came pipelines, which act as containers for processors. When initializing a channel, install the channelHandler in the pipeline in order to execute channelHandler sequentially.
! [](https://pic3.zhimg.com/80/v2-2737481fee9a7dd0236f1cb61e823293_720w.png)
Within a Channel, there is only one Channel pipeline. The pipeline is created when the Channel is created. ChannelPipeline contains a list of ChannelHanders, and all ChannelHandlers are registered with ChannelPipeline.
5.8 ChannelHandlerContext
In Netty, the Handler Handler is defined by us and is implemented by integrating the inbound or outbound Handler as described above. If we want to get a Pipeline object or a channel object in Handler, how do we get it?
So Netty designed this ChannelHandlerContext object, you can get channel, pipeline and other objects, you can read and write operations.
! [](https://pic4.zhimg.com/80/v2-61d526a0cdd6037b06b63e1307048317_720w.png)
Through the class diagram, ChannelHandlerContext is an interface with three implementation classes.
The ChannelHandlerContext is actually a linked list in the pipeline. Look at a piece of source code to understand:
Protected DefaultChannelPipeline(Channel Channel) {this.channel = ObjectUtil.checkNotNull(channel, "channel"); succeededFuture = new SucceededChannelFuture(channel, null); voidPromise = new VoidChannelPromise(channel, true); Tail tail = new TailContext(this); head = new HeadContext(this); head.next = tail; tail.prev = head; }Copy the code
Here’s a picture to make it a little clearer:
! [](https://pic2.zhimg.com/80/v2-f74de0a1d853d01fc46f717d4706a7af_720w.png)
5.9 EventLoopGroup
Let’s take a look at the EventLoopGroup class diagram:
! [](https://pic2.zhimg.com/80/v2-3d826a5a72e611c31b30c10ee10a7bbb_720w.png)
This includes the commonly used implementation class NioEventLoopGroup. The OioEventLoopGroup was also used in the previous example.
From the Netty architecture diagram, you can see that the server needs two thread groups to work together, and the interface of this thread group is the EventLoopGroup.
Each EventLoopGroup contains one or more Eventloops, and each EventLoop maintains a Selector instance.
5.9.1 Implementation principles of the polling mechanism
We might as well see a DefaultEventExecutorChooserFactory source code:
private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; @override public EventExecutor Next () {//idx.getAndIncrement() = idX ++, Return executors[IDx.getandIncrement () & Executors. Leng-1]; }Copy the code
This code can determine the execution mode is polling mechanism, next debug debugging:
! [](https://pic3.zhimg.com/80/v2-f00604124bc547b7a3fb151e9c9a50db_720w.png)
It also has a judgment here, if the number of threads is not 2 to the N, then use the modulus algorithm to achieve.
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
Copy the code
Author: Omega has no gar
The original [link] (https://developer.aliyun.com/article/769587?utm_content=g_1000170046)
This article is the original content of Aliyun and shall not be reproduced without permission.