This chapter includes:
1) Architecture design and technical point of Netty
2) Channel,eventloop and Channelfuture
3) Channelhandler and Channelpipeline
4) Bootstrap
In the first chapter, we describe the history of Java's development in high-performance network programming and the accumulation of the technical foundation of the network, which provides a good atmosphere for the analysis of Netty's core components and building blocks.
In the second chapter, we expanded our scope of discussion, we built our first Netty-based application by building a simple server-side and a client to let us know how to start Netty, It also gives us a hands-on experience of how the most important Channelhandler APIs for business logic processing are used, and you have verified that the Netty development environment has been built successfully.
In the next part of the book, we learn to explore Netty through two perspectives, one from the Java class file, one to learn from the perspective of Netty as a framework, the two angles are different, but it is very close, for Netty, These two angles are extremely important for writing efficient, reusable, and maintainable code.
From a higher point of view, Netty solves two different areas of the problem, these two areas we generally call the technical domain and the architecture domain, first of all technically, Netty is a Java-based NiO asynchronous event-driven framework, The ability to guarantee the performance and scalability of applications at high loads, in fact, at the architectural level, Netty contains a number of design patterns to decouple the application's logical processing from the network layer, simplifying the code while ensuring maximum testability, modularity, and maximum reproducibility of the code.
Because of Netty's technological superiority and superior design, we will delve deeper into the unique and wonderful components of Netty, and examine how these components work together to build a framework for such a good user experience, by understanding these design and technical intentions, We will derive great benefits from the use of Netty, keeping this objective in mind, and we will go deep into the components of the netty that we have previously explained.
3.1 Channel, EventLoop, and Channelfuture
In the next few sections, we will explain the details of channel,eventloop,channelfuture these components, which can be seen as a few examples of Netty network module abstraction
1) Channel-------keyword: Sockets
2) eventloop-------Keyword: control flow, multi-threading, concurrency
3) channelfuture-------Keywords: asynchronous notification
3.1.1 Interface Channel
Basic I/O operations (BIND (), connect (), read (), and write ()) are all dependent on the most primitive support for network transport, and the most basic constructs for a Java-based network model are sockets, The Netty Channel interface provides an API that can greatly reduce the complexity of using the socket directly, but the channel is just an original interface, and there are a number of specific implementation classes that implement the previously defined methods in the channel, with a list of specific classes:
3.1.2 Interface EventLoop
EventLoop defines the core abstractions that are processed when events occur throughout the lifetime of the connection, and we will introduce eventloop in detail in the multithreaded model of the seventh chapter Netty, Now all we need to know is the relationship between Channel,eventloop,thread and Eventloopgroup shown in the 3.1 figure.
These relationships include:
1) A eventloopgroup contains multiple EventLoop
2) A eventloop binds only one thread in a lifetime
3) All I/O operations on a channel will be handled by the specified thread EventLoop bound
4) A channel is registered only to a single eventloop in its life cycle
5) A eventloop gives can be assigned multiple channel
Note that this design, when I/O operations in a channel is performed by the same thread, virtually eliminates the need for synchronization
3.1.3 Interface channelfuture
As we explained earlier, all I/O operations are asynchronous in Netty because the result of the operation does not return immediately, and we need a way to determine the result at a later time, for this purpose, Netty provides channelfuture, Its AddListener () method registers a Channelfuturelistener to notify whether the operation has been completed (whether successful or not)
More on Channelfuture we can think of channelfuture as a placeholder for the result of an operation returning in the future, although it is not possible to accurately predict its results at execution time, because the result of its return depends on some unknown factors, One thing is certain, however, that the result will certainly be executed, whether right or wrong, and that all operations belonging to the same channel are guaranteed to be executed in the order in which they are called.
In the seventh chapter, we will discuss EventLoop and Eventloopgroup in depth.
3.2 Channelhandler and Channelpipeline
Now we'll explore in detail the components that govern the flow of data and the execution of the application's business logic
3.2.1 Interface Channelhandler
From an application developer's point of view, the most important Netty component is channelhandler, as it is used as a service container to handle the business logic of input and output data because the methods in Channelhandler are triggered by events on the network. In fact, Channelhandler is committed to handling all event actions, such as transferring data from one end conversion format to the other or handling exception operations
For example, Channelinboundhandler is a subclass of Channelhandler, and we often implement this interface, which allows you to receive input data and then process the data according to your application's business logic. You can also refresh data from a channelinboundhandler when you send a response feedback to a connected client, the business part of the application typically has one or more Channelinboundhandler
3.2.2 Interface Channelpipeline
A channelpipeline provides a container for the Channelhandler chain, an API that defines the method of propagating the input and output of the data in the chain, and when a channel is created, it is automatically assigned to the channelpipeline that belongs to it. , the Channelhandler is loaded into the channelpipeline in accordance with the following several principles:
1) A channelinitializer is registered on the serverbootstraping
2) When the Channelinitializer Initchannel method is called, Channelinitializer loads the custom channelhandler onto the pipe.
3)Channelinitializer it to remove itself from the Channelpipeline
Let us delve deeper into the symbiotic relationship between Channelpipeline and Channelhandler, to detect the event flow when the data is input and output
Channelhandler is designed specifically to support the use of some users, and you can think of it as a container for normal code to handle data flowing in and out of Channelpipeline, 2 Channelhandler of derivative classes were shown in 3.2
The actions to be done in the pipeline have been defined at the time of Channelhandler initialization, and then loaded into channelpipeline , which receive events during the application's startup run phase, Perform the business logic that they have already implemented, and after processing is finished, the data is transferred to the next handler in the chain, in the order that they are added to the chain, and for these practical purposes, the Channelhandler is organized so that we define Channelpipeline
Figure 3.3 illustrates the different differences in input and output data in the Netty application, from the client's point of view, if the direction of the move is defined as outbound from the client to the server-side event, the opposite is defined as inbound
Figure 3.3 also shows us that the input and output processing can be loaded in the same pipeline, if any information or input stream is read, its process is this, starting from the head of the pipe and then through the first Channelinboundhandler, this processing may change the information or data inside, it depends entirely on your specific Data will be passed through the other handler in the chain, the final data will reach the end of the data, until now, the data has been processed
The behavior characteristics of the output data in theory is the same as the input, the output data stream from the tail into the chain of channeloutboundhandler processing reached the head, at this time, the output data reached the network transmission end, in this figure is specifically the socket, the typical output data stream That happens when the trigger is written.
TIPS: More details on inbound and outbound processing
Each event can be assigned to its next processor in the current chained processor via Channelhandlercontext, which can be passed as a parameter to each method, because you sometimes want to ignore the ones you don't care about, Netty provides an abstract class of Channelinboundhandleradapter and Channeloutboundhandleradapter, These two classes are easily filtered to some of the less interesting event handlers by calling the appropriate method in Channelhandlercontext, and you can override these methods yourself to make your data flow only handled by the processor you're interested in.
Given that the input and output operations are different, then you would have doubts about what would happen if the two types of processing were placed in the same channelpipeline, although the input and output handler are inherited from Channelhandler, But Netty still distinguishes their implementations, one called Channelinboundhandler, the other called Channeloutboundhandler, and the benefit of this is to ensure that the data is handled by the same processor in the process of transmission processing
When a channelhandler is added to the channelpipeline, it will be assigned a corresponding Channelhandlercontext, which represents Channelhandler and Channelpipeline, although the channelhandlercontext object can be directly fetched to the underlying channel but in most cases it is used to write output data
In Netty, there are two ways to send messages, the first you can write data directly to the channel, and the second you write input to the channelhandlercontext associated with Channelhandler , The first method causes the message to start at the end of the Channelpipeline, and the latter method causes the information to be processed from the next processor in Channelpipeline
3.2.3 A closer look at Channelhandlers
As we have said before, Channelhandler has many different types, each of which is specificChannelhandlerfunction is defined by its parent class or superclass, Nettyprovided in the form of a suitable classA large number of default processing implementations, the intention is to simplify the development of application processing logic, which you have already seen, everyThe Channelhandler in the pipeline prepares for the event that passes in to the next processor, which is usually done automatically by the adaptation class or their subclasses, and you just need to rewrite the method you need to handle.
TIPS: Adapters?
Some of the appropriate classes are used to reduce our own writing some of the more pale custom implementation classes, because these adaptable classes provide some default implementations for all methods in the corresponding interface
When creating your own custom processor, some of the appropriate classes are often called
1) Channelhandleradapter
2) Channelinboundhandleradapter
3) Channeloutboundhandleradapter
4) Channelduplexhandleradapter
Next, we'll look at Channelhandler's sub-objects: Subclasses of Encoders,decoders and Channelinboundhandleradapter Simplechannelinboundhandler <T>
3.2.4 Encoders and decoders
When you use Netty to receive or send information, the data conversion is inevitable, the input to obtain the data need to decode, decoding the meaning of the byte into another format, generally Java objects, if it is data output, then the process is the opposite, the data needs to be encoded, Encoding is the object into bytes, the reason for conversion is also very simple, because the network transmission of data consistent byte
A variety of abstract classes provide decoding and encoding capabilities, depending on the situation, using different decoding code, your application may sometimes need to use only the middle state of the format, rather than directly to the immediate conversion of your information into bytes, you still need to code, which can be derived from the parent class, To determine the most appropriate, you can customize a simple naming convention
In general, the basic classes will have tool classes like Messagetobyteencoder and messagetobyteencoder such as encoding and decoding, for some specific object classes, You can find similar Protobufencoder and Protobufdecoder codec classes to support Google's protocol buffers
Strictly speaking, other processors can also encode and decode, but because there are some adaptation processing classes to simplify the creation of channel processors, All codecs provided by Netty are either inherited Channelinboundhandler or inherited Channeloutboundhandler
You will find that for the input of the data, we generally rewrite the method of Channelread, which is called when the data is read from the channel, and it calls the Decode method provided by the decoding class, decodes to the next handler in the pipeline, and for the data output, The process is the opposite.
3.2.5 Abstract class Simplechannelinboundhandler
In most cases, you need a processor to receive a message that needs to be decoded, and then manipulate your business logic to work on that data, in order to create a channelhandler of this function, you only need to inherit Simplechannelinboundhandler <T> Here's the T worth is the type of Java object you want to convert the information into, and in this process you need to rewrite one or more methods and get a channelhandlercontext reference. This application is used as a parameter for each method by the developer
The most important method of this processor is to channelRead0 this method, which depends entirely on your implementation when the current I/O thread is not blocked, and the detailed information we will discuss next
3.3 Bootstrapping
Netty's bootstrap class provides the ability to configure the application network layer, typically including two blocks of functionality, the first is to bind a process to a given port, and the second is to connect another process that is running on a specific port
In general, we regard the first approach as starting the server and the second as the client, which is simple and convenient, but it is a little masking of the two important factors of "server" and "client" that represent different network behavior. One is to listen to the connection one is to establish one or more processes
connection-oriented protocols Please keep your heart in mind and be cautious when you say "CONNECTION" because CONNECTION refers only to connection- based protocols, such as TCP protocol, which guarantees the transmission sequence of information for two terminals
Therefore, there are two types of startup classes, one for the client side, and the other for the server side, called Serverbootstrap, regardless of what type of protocol your application uses, and whatever the form the data is performing, The only thing bootstrap to do is to determine whether it is running a client or server at this point, and table 3.1 compares the differences between the two startup classes
The first difference between the two types of bootstrap is that the Serverbootstrap bound port, because the server side must listen to the connection, and Bootstrap is used on the client side of the application to connect a remote host. The second difference is significant, the client's bootstraping only need a ecentloopgroup, two serverbootstrap but need two, even if these two are the same instance, then why? Because the server side needs two partitions of channels, the first one contains a serverchannel for the code server side of its own listening socket, bound to a local port, The second processing of all the created channel from client-side connections and server-side reception, figure 3.4 illustrates this model, which is why 2 Eventloopgroup is required
The eventloopgroup associated with Serverchannel assigns a eventloop, the EventLoop is responsible for creating the channel to handle the connection request for the new connection, once the connection is established successfully, The second Eventloopgroup assigns an object's eventloop to each newly created channel
3.4 Summary
In this chapter, we discuss Netty from two perspectives of technology and architecture, and we review and delve into some of the concepts and Netty components described in our previous chapters, especially channelhandler,channelpipeline,bootstraping
We also introduced some derivative classes of Channelhandler, introduced the decoding code, and described their implementation in the network byte transmission is the transformation of the data.
Many of the sections that follow will introduce these components in more depth, and an overview of the current presentation will help you build a blueprint for the Netty architecture in your mind.
In the next section, we will show you some of the network transport services provided by Netty, and will tell you to choose the right transfer service based on your application scenario to make the best use of it.
Netty in Action (vii) Chapter III Netty components and design