This article is the fifth in the Netty series

In the last article, we gained a thorough understanding of the relationship between I/O multiplexing, Java NIO packages, and Netty.

So far, we’ve moved from the I/O model to the Netty framework. In this process, the basic answer to Netty is what, why to use Netty and other pre-questions. Provides us with the most original background knowledge to learn Netty.

With that as a foundation, we can begin to unravel the mystery of Netty.

This article will take about 5 minutes to read and will focus on the following questions:

  • How to write a Server Demo using Netty
  • See the logical architecture of Netty from Demo

1. Write a Server Demo

1.1 Demo implementation based on Reactor model

If you have never used Netty before, it is essential to learn about Server side demos written in Netty.

Remember the “Reactor model” we talked about in our last post? The primary Reactor listens to the server socket separately, accepts the new connection, registers the established SocketChannel to the specified secondary Reactor, and then performs the read, write, and distribute events. The business processing is thrown to the worker thread pool.

We are in accordance with this model, using Netty to write a server procedures.

Go straight to the code!

A simple custom ChannelHandler class that uses custom business processing logic:

A server startup class containing Bootstrap:

public class EchoServer { private int port; public EchoServer(int port) { this.port = port; } public static void main(String[] args) throws Exception { new EchoServer(8833).start(); } public void start() throws Exception {//1.Reactor mainGroup = new NioEventLoopGroup(); EventLoopGroup childGroup = new NioEventLoopGroup(); ServerBootstrap ServerBootstrap b = new ServerBootstrap(); b.group(mainGroup, ChildGroup). Channel (NioServerSocketChannel. Class) / 2.1 / set the NIO channel. The localAddress (new InetSocketAddress (port)) / / 2.2 ChildHandler (new ChannelInitializer<SocketChannel>() {//2.3 When initializing a channel, InitChannel (final SocketChannel SocketChannel) {socketchannel.pipeline () .addLast("codec", new HttpServerCodec()) .addLast("compressor", new HttpContentCompressor()) .addLast("aggregator", new HttpObjectAggregator(65536)) .addLast("handler", new EchoServerHandler()); //2.4 Add custom service logic ChannelHandler}}); ChannelFuture f = b.bind().sync(); Println ("Http Server started, Listening on "+ port); f.channel().closeFuture().sync(); } finally { mainGroup.shutdownGracefully().sync(); childGroup.shutdownGracefully().sync(); }}}Copy the code

Curl curl curl curl curl curl curl curl curl curl

Demo done!

Netty made it easy to implement the master-slave Reactor schema, which I found too complex to implement with the Java NIO package.

Just create two EventLoopGroups and bind them to the bootstrap ServerBootstrap.

MainGroup is the primary Reactor, and childGroup is the secondary Reactor. Each uses a different NioEventLoopGroup, the primary Reactor handles Accept, and then registers the Channel with the secondary Reactor, which is responsible for all I/O events during the Channel’s life cycle.

1.2 Demo analysis

As you can see from the Demo code above, for all server-side programs written in Netty, you need at least two parts:

  • At least one ChannelHandler
  • Bootstrapping

1) ChannelHandler

This component is used to process data sent from the client, including codec, custom business logic processing, and so on.

There are a lot of implementations for ChannelHandler. In the Demo, we used several native Netty handlers, including HttpServerCodec, HttpContentCompressor, and HttpObjectAggregator. We also used a custom EchoServerHandler.

As you can see, it is very important and very convenient to use the Handler. We will expand on this in a future article.

2) can

Start the code section. Used to configure server startup parameters, including listening ports, server thread pool configuration, network connection properties configuration, ChannelHandler configuration, and so on.

Combined with the Demo, it can be divided into the following steps:

  • Create a ServerBootstrap instance to boot.
  • Create one instance of NioEventLoopGroup (two when we used the master-slave Reactor pattern) to handle events, such as accepting a new client connection, reading or writing data, and so on.
  • Specify a port that can be used as a listening port for the server.
  • Each Channel is initialized with a series of ChannelHandlers, including a custom business logic implementation’s channelHandler.
  • Call serverbootstrap.bind () to actually trigger the startup.

2. Logical architecture of Netty

From the above Demo, we have a general idea of Netty usage.

Let’s take a look at Netty’s logical architecture based on some of the components used in the Demo.

Combining our Demo with this logical architecture diagram, we can tease out the flow of each component:

  • The server uses ServerBootstrap to boot and bind the listening port
  • There are two components: Main EventLoopGroup and Child EventLoopGroup. The main EventLoopGroup listens for network connection events. When a new network connection is made, a Channel is registered with the Child EventLoopGroup.
  • The Child EventLoopGroup is assigned an EventLoop to handle read and write events for the Channel.
  • When the client initiates an I/O read/write event, the server EventLoop reads the data, and then sequentially triggers channelhandlers for data processing through the ChannelPipeline.
  • Client data is passed in turn to the ChannelPipeline’s ChannelInboundHandler, where it is processed in one handler and passed to the next.
  • When data is written back to the client, it is passed to the ChannelPipeline’s ChannelOutboundHandler in turn. After processing in one handler, it is passed to the next handler and returned to the client.

This is the logical architecture of each component of Netty. We only need to understand the general framework for the moment. We will introduce each component in detail later.

A few common problems are summarized here:

A Channel is a carrier of network communication. It provides basic apis for network I/O operations, such as register, bind, connect, read, write, flush, and so on.

The Netty implementation of channels is based on JDK NIO channels, providing a higher level of abstraction and masking the underlying Socket.

2) what is ChannleHandler and ChannelPipeline

ChannelHandler is used to process the data sent by the client, including codec, custom service logic, and so on.

ChannelPipeline assembles channelHandlers. When an I/O read or write event is triggered, ChannelPipeline calls the list of ChannelHandlers in turn to intercept and process the Channel’s data.

3) What is an EventLoopGroup?

An EventLoopGroup is a thread pool that implements the Netty Reactor thread model. It receives I/O requests and allocates threads to process the requests. Its implementation class NioEventLoopGroup, which we used in the demo, is the most recommended threading model in Netty.

We also implemented the master-slave Reactor pattern by building the Main EventLoopGroup and Child EventLoopGroup.

4) What is the relationship between EventLoopGroup, EventLoop and Channel?

An EventLoopGroup usually contains one or more Eventloops.

EventLoop Processes all I/O events within a Channel’s life cycle, such as ACCEPT, Connect, Read, and write.

EventLoop is bound to one thread at a time, and each EventLoop handles multiple channels.

Bibliography: Netty in Action

See the end, the original is not easy, point a concern, point a like it ~

Reorganize the knowledge fragments and construct the Java knowledge graph: github.com/saigu/JavaK… (Easy access to historical articles)