Netty is an excellent network programming framework, which is the secondary encapsulation of NIO. This article will focus on the analysis of the startup process of Netty client, and in-depth understanding of how to use NIO programming client.
This article is a demonstration of my problem-based heuristic source code reading skills. Please begin this article with the following questions:
-
How does Netty add client events to the event chain?
-
Does Netty client need to register read events at startup?
-
Does Netty client need to register write events at startup?
-
What are the key points you need to implement if you write a client based on NIO?
The question above could also be asked: Which events should be registered when implementing a client using NIO?
This section will learn the startup process of the Netty4 client in detail, starting with an example using the Netty client.
1. Netty client example
An example Netty client is as follows:
Code @1: Create NIO EventLoopGroup, NIO event poller.
Code @2: Create Bootstrap instance, Netty client manipulation tool class.
Code @3: specifies the create Channel type, and the client is NioSocketChannel.
Code @4: Through the option method to set the network related attributes, can be called many times.
Code @5: Add the client-side event handler chain by calling the handler method. Using the anonymous ChannelInitializer class, the event Handler chain usually contains an encoder, a decoder, and a business processing Handler(an entry point for business processing).
Code @6: Call the connect method to establish a connection asynchronously. Call sync() to synchronize the call and wait for the connection to be successfully established before returning.
Code @7: Create a Close Future and synchronously wait for the Close event to arrive.
Code @8: Safely and elegantly close the event thread group. Connections are not used this way during specific events, but instead are cached, such as using a Map to cache by IP, using the same connection for requests to the same IP, and calling methods when the connection needs to be disconnected.
Tips: The above is just a Demo level example, the following will give industrial Netty use actual combat.
2. Netty client startup process
This article tries to reveal the Netty client startup process, so that we can thoroughly grasp the implementation details from the bottom, facilitate the better application of Netty, especially corresponding to the obstacle to play a good skill reserve.
Netty client startup process:
It ends up calling its doConnect method.
@1: call initAndRegister to initialize and register with the event selector. NIO needs to register read and write events after creating channels.
@2: initAndRegister is asynchronous, or rather Future. If it is done, the doConnect0 method is called to perform the connection.
Code @3: Otherwise register an event listener on the Future and call the connection after the registration is successful.
The above two important steps are covered in detail next, and a good example of using the Future pattern is provided here.
2.1 Channel Registration
Before connecting, the client needs to register the channel with an event Selector, as shown in the following figure:
The key is as follows: code @1: Call init to pass initialization, more on that later.
Code @2: will be registered in the event poller.
2.1.1 Channel Initialization
The code for channel initialization is as follows:
Code @1: add a user-defined Handler to the ChannelPipeline. This is critical: the client starts with the Handler method of Bootstrap. Codes @2 and 3 mainly set network options and properties.
Question to consider: How does Netty add multiple client-defined handlers to the event chain?
The essence of the problem is when the initChannel method of the user-defined ChannelInitializer is called.
To answer this question, we need to check out ChannelInitializer.
A few key points from the diagram above:
-
The handlerAdded method is called when a Handler is added to a ChannelPipeline. The logic is to call the initChannel method if the channel is already registered with the event Handler.
-
Called when a channel is registered with an event Selector.
-
The initChannel method supports idempotent, ensuring that the initChannel method of a channel will not be called more than once.
2.1.2 Channel Registration
Entry method for channel registration: SingleThreadEventLoop.
The final entry point for NIO is the Register method in AbstractChannel’s AbstractUnsafe inner class.
A very important Netty programming trick is revealed here: a channel will hold an EventLoop, and if the thread calling the channel’s method is not an EventLoop, the task will be submitted to the EventLoop for execution.
Next, we’ll focus on the register0 method, which is very critical.
Code @1: Registers the underlying NIO level and finally calls AbstractNio’s doRegister.
Note: channel registration, with OP 0, does not focus on any events (read, write, etc.), so the question is, when do read and write events register?
@2: Trigger the handlerAdd event, because in the introduction of the ChannelInitializer handlerAdd event, the condition is that the channel is registered. If the channel is not registered, the task will be suspended.
Code @3: Propagates channel registration events, which are propagated after the underlying NIO’s register method is called to register a channel with an event Selector.
Code @4: Propagates channel activation events if the channel is active and registered for the first time.
Code @5: If the channel is not registered for the first time and automatic registration of read events is enabled, the channel is automatically registered for read events. The beginRead method will eventually call AbstractNioChannel’s following methods, using NIO’s selectionKey.interestOps method at the bottom.
This concludes the channel registration process.
In NIO, if you do not register read events, data will not be read from the underlying network, then the corresponding client will not process the response package, relative to the server will not parse the request package, that is, cannot complete the request and response, so in NIO programming, channels must register read events.
The logic for registering read events is basically complete, but the initial registration only propagates channelActive events. When do you register read events?
Principle After the channelActive event is propagated and the automatic read event is automatically registered, its code is in the channelActive method of internal class HeadContext in DefaultChannelPipeline, and its code is captured as follows:
2.2 Connecting the Server
Take a look at the client startup core method:
After the channel has been initialized and registered with the event selector, it is then necessary to initiate a connection operation to the server and finally call the doConnect0 method, whose basic call chain is shown below:
So let’s focus on an in-depth trace from AbstractNioUnsafe’s Connect method.
The key points of the above method are as follows:
-
Gets whether the channel is active.
-
Calling the internal doConnect to complete the connection calls the underlying NIO-related methods from which you can learn how to write NIO connection code, more on that later.
-
Since NIO is non-blocking, doConnect does not indicate that the channel has successfully connected to the server, so a scheduled task (connection timeout) needs to be started for tracing.
Let’s take a closer look at how to write connection code using NIO.
@1: The client connects to the server using a random port. To bind to a specified port, call SocketChannel’s bind method.
Code @2: Call the connect method of the underlying NIO. Note that this method does not block. Return false to indicate that the connection is still in progress.
Code @3: If it is connected, then register OP_CONNECT event, such as successful connection will receive the corresponding event.
After an OP_CONNECT event is registered, the OP_CONNECT event is handled exclusively in the event processing flow of the event selector NioEventLoop:
NIO mode code finally calls NIO SocketChannel’s doFinishConnect method:
In non-blocking mode, this method channel will return false if the connection is not successfully established. Because this method channel is selected based on the event, it will be triggered after the connection is successfully created, so it is simply to verify whether the connection is successfully established.
Finally, the channel is successfully established, and the channel activation event needs to be triggered, as shown in the following figure:
AbstractNioChannel#fulfillConnectPromise
Article so far at the beginning of the department put forward problems have been targeted answers, in order to force readers friends door can really understand the beginning of the problem, suggest readers to summarize, really do with the problem of reading source code, of course, can add the author’s private letter: DINGWPMZ, common exchange and discussion.
This article is introduced here, please light up to see, like, forward, message is the biggest encouragement to me.
TOP3 best articles of the yearCopy the code
Here’s some advice from a 10-year IT veteran for new employees
How can programmers increase influence
How to read source code efficiently