Introduction to the

We often use a browser to access a Web page and get relevant information, usually using HTTP or HTTPS protocol, these protocols are essentially IO, the client request is In, the server return is Out. But in the current framework of the agreement, it does not fully meet all our needs. For example, using HTTP to download large files, may require a long connection wait. We also know that there are various types of IO, including synchronous IO, asynchronous IO, blocking IO, and non-blocking IO. Different IO modes have different performance, and Netty is an asynchronous event-driven NIO framework.

This series of articles will explore the use of Netty in detail, through a specific combination of principles and examples, let you understand and understand the charm of Netty.

Netty is introduced

Netty is an excellent NIO framework. Your first image of IO should be quite complex, especially when dealing with various HTTP, TCP, and UDP protocols. But Netty provides a friendly encapsulation of these protocols, allowing for fast and concise IO programming. Netty is easy to develop, excellent performance, stability and flexibility. If you want to develop high performance services, you can always use Netty.

The latest version of Netty is 4.1.66.Final. In fact, this version is the most stable version officially recommended.

If you want to use it in your project, you can introduce the following code:

Ty < dependency > < groupId > io.net < / groupId > < artifactId > netty -all < / artifactId > < version > 4.1.66. The Final < / version > </dependency>Copy the code

Let’s take a look at the most simple example of how netty works.

Netty’s first server

What is a server? A program that can provide services externally is called a server. Set up a server is the first step of all external services, how to use netty to set up a server? Server is mainly responsible for processing a variety of service requests, netty provides a ChannelInboundHandlerAdapter class to deal with this kind of request, we only need to inherit this class.

In NIO, each channel is the channel through which the client and server communicate. ChannelInboundHandlerAdapter defines some events and possible case on this channel, as shown in the figure below:

As shown in the figure above, many events can occur on a channel, such as establishing a connection, closing a connection, reading data, reading completed, registering, unregistering, and so on. These methods are all can be rewritten, we only need to create a new class, inheritance ChannelInboundHandlerAdapter can.

Here we create a new FirstServerHandler class and override the channelRead and exceptionCaught methods. The first method reads messages from the channel and the second method handles exceptions.

public class FirstServerHandler extends ChannelInboundHandlerAdapter { @Override public void ByteBuf in = (ByteBuf) MSG; ByteBuf in = (ByteBuf); Try {log. The info (" messages are received: {} ", in the toString (io.net ty. Util. CharsetUtil. US_ASCII)); }finally { ReferenceCountUtil.release(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// Exception handling log.error(" exception occurred ",cause); ctx.close(); }}Copy the code

In the example above, we receive the message and call the release() method to release it without actually processing it. Calling the release method is a common practice after the message has been used. The above code casts MSG to ByteBuf. If you don’t want to convert MSG to ByteBuf, you can use it like this:

Try {/ / message processing} finally {ReferenceCountUtil. Release (MSG); }Copy the code

In the exception handling method, we print out the exception information and close the exception context.

With Handler, we need to create a new Server class that uses Handler to create channels and receive messages. Let’s take a look at netty’s message processing flow.

In Netty, IO processing is implemented using multi-threaded Event loops. EventLoopGroup in Netty is an abstract class of these event loops.

Let’s take a look at the class structure of EventLoopGroup.

As you can see, EventLoopGroup inherits from EventExecutorGroup, which in turn inherits from the JDK’s ScheduledExecutorService.

So an EventLoopGroup is essentially a thread pool service. It is called a Group because it contains a number of Eventloops that can be traversed by calling the next method.

An EventLoop is used to process the IO information in the channel registered with the EventLoop. An EventLoop is an Executor that executes by continuously submitting tasks. Of course, an EventLoop can register multiple channels, but this is not usually the case.

An EventLoopGroup consists of multiple Eventloops into a Group. Through the next method, you can iterate over the Eventloops in the Group. In addition, EventLoopGroup provides several register methods to register a Channel into the current EventLoop.

As you can see from the figure above, the return result of register is a ChannelFuture, which can be used to obtain the execution result of asynchronous tasks. Similarly, ChannelFuture is also an asynchronous result bearer. You can block a Future until the execution result is obtained by calling the sync method.

As you can see, the register method can also pass in a ChannelPromise object, ChannelPromise which is a subclass of both ChannelFuture and Promise, which is a subclass of Future, It is a special Future that controls the Future state.

There are many subclasses of EventLoopGroup, but here we use NioEventLoopGroup, and Nio uses selectors to select channels. Another feature is that NioEventLoopGroup can add child Eventloopgroups.

For the NIO server program, we need two groups. One Group is called bossGroup, which is used to monitor connections, and the other Group is called Worker Group, which is used to handle connections accepted by the boss. These connections need to be registered with the Worker Group for processing.

ServerBootstrap: ServerBootstrap: ServerBootstrap: ServerBootstrap: ServerBootstrap: ServerBootstrap: ServerBootstrap

// Create two eventLoopGroups to handle connections and messages EventloopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new FirstServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.ind (port).sync();Copy the code

The FirstServerHandler we originally created was added as a childHandler handler when the Channel was initialized.

In this way, when a new channel is created, FirstServerHandler will be used to process the data for that channel.

In the example above, we also specified some channelOptions, which are used to set some properties of the channel.

Finally, we bind the corresponding port and start the server.

Netty’s first client

Now that we’ve written the server and started it, we need a client to interact with it.

If you don’t want to write code, you can use Telnet localhost 8000 to interact with the server, but here we want to use Netty API to build a client to interact with the server.

The process for building a Netty client is basically the same as the process for building a Netty server. First need to create a Handler is used to process specific messages, similarly, we also inherit ChannelInboundHandlerAdapter here.

In the previous section about the ChannelInboundHandlerAdapter there are many methods, can according to their own business need to rewrite, here we hope when the Channel active sends a message to the server. You need to override the channelActive method, and you also want to do some handling of exceptions, so you also need to override the exceptionCaught method. You can override the channelRead method if you want to process the message when the channel reads it.

Create FirstClientHandler as follows:

@Slf4j public class FirstClientHandler extends ChannelInboundHandlerAdapter { private ByteBuf content; private ChannelHandlerContext ctx; @Override public void channelActive(ChannelHandlerContext ctx) { this.ctx = ctx; content = ctx.alloc().directBuffer(256).writeBytes("Hello flydean.com".getBytes(StandardCharsets.UTF_8)); // Send the message sayHello(); } @override public void exceptionCaught(ChannelHandlerContext CTX, Throwable cause) {// Override public void exceptionCaught(" exception ",cause); ctx.close(); } private void sayHello() {// write a message to the server ctx.writeAndFlush(content.retain()); }}Copy the code

In the above code, we first request a ByteBuff from the ChannelHandlerContext and then call its writeBytes method to write the data to be transmitted. Finally, CTX’s writeAndFlush method is called to output the message to the server.

The next step is to start the client service, where we set up two NioEventLoopgroups, both for selecting a channel and for reading messages from a channel. On the client side, there is no such problem; a NioEventLoopGroup is all that is needed.

The server uses ServerBootstrap to start the service, and the client uses Bootstrap. The startup logic of the server is basically the same as that of the server:

EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new FirstClientHandler()); }}); ChannelFuture = b.connector (HOST, PORT).sync();Copy the code

Run the server and client

With the above preparations in place, we are ready to go. Run the server first, then the client.

If there are no problems, it should output something like this:

INFO com [nioEventLoopGroup - 3-1]. The flydean01. FirstServerHandler - receive the message: Hello, flydean.comCopy the code

conclusion

A complete server, client example is complete. To sum up the netty workflow, for the server side, we first set up a handler for the actual processing of messages, and then use ServerBootstrap to group eventloops and bind ports for startup. On the client side, you also need to set up a handler to process the message, then call Bootstrap to group the EventLoop, and bind the port to start.

With the above discussion in mind, you can develop your own NIO services. Is it simple? Stay tuned for an in-depth discussion of Netty’s architecture and the principles behind it.

This article is available at www.flydean.com

The most popular interpretation, the most profound dry goods, the most concise tutorial, many you do not know the small skills waiting for you to find!

Welcome to follow my public number: “procedures those things”, understand technology, more understand you!