Here are some questions to consider before reading this article:
- Why is NIO not suitable for scenarios such as file uploads
- How does NIO avoid the impact of a large data transfer connection on other requests
- How does NIO handle semi-shutdown
1. Read the event summary
The operation of a Read event on a SocketChannel is different from that on a ServerSocketChannel. In a SocketChannel, the Read event corresponds to a data Read, while in a ServerSocketChannel, the Read event is encapsulated to receive a connection request from a client.
The NIO Read event entry is in the processSelectedKey method of the NioEventLoop.
The core entry is the read method for broadening, and the class hierarchy for broadening is shown below:
- AbstractNioByteChannel$NioByteUnsafe#read read event processing flow corresponding to SocketChannel, i.e., IO read processing implementation.
- AbstractNioMessageChannel $NioMessageUnsafe# read ServerSocketChannle corresponding read event handling process.
These two processes are described separately next.
2. I/O read events follow the processing flow
AbstractNioByteChannel AbstractNioUnsafe inner class AbstractNioUnsafe read method is implemented by the IO read event. Next, this method will be analyzed to see how Netty handles the IO read event.
Step1: If the automatic registration read event is not enabled, the read event will be cancelled after each read time processing. The automatic registration is enabled by default.
Tip: If a channel does not register read events, it will not be able to read data from the channel, that is, it will not be able to process requests or receive responses.
If automatic read events are not enabled, the application needs to manually call the channel’s read method if needed.
Cancel the read event, Netty based on NIO gives a very standard implementation, basic can be used on the spot template code:
The key point of its implementation: first determine whether the key-value pair is valid, and then cancel the registration through the bit operation.
Step2: create a memory allocator that accepts the cache. There are two key points:
- maxMessagesPerRead
Each channel in a read event processing can call the underlying Socket read the number of times, the default is 16 times, the design philosophy is to avoid a channel here need to read too much data, thus affecting other channels of data read, because multiple channels in an event selector read events are executed serially.
- RecvByteBufAllocator
The memory allocation strategy of accepting buffer can be divided into fixed size allocation (capacity expansion when insufficient) and dynamic change (memory size suitable for dynamic conditions according to the size of historical allocation). The main design philosophy here is to make reasonable use of memory, reduce capacity expansion and improve the efficiency of memory allocation and use.
Step3: process read events in a loop, maxMessagePerRead at most. Next, discuss the place of the single read event
Step4: perform an IO read processing, which has the following key points:
-
First, a ByteBuf, commonly known as the receive cache, is allocated to hold the content read from the network.
-
Get the number of bytes that can be written to the allocated cumulative cache.
-
Call the underlying network read API to read data from the network card, NIO read implementation is as follows:
That is, the SocketChannel in the NIO is called to read the data, and the return parameter is the number of bytes read from the nic this time. If the number of bytes read is less than 0, the peer channel is closed, and the peer channel needs to be closed, for example.
When a batch of data is read, a channelRead event is propagated to the event chain via the event propagation mechanism, triggering subsequent processing of the batch.
Step5: determine whether the passage needs to continue reading based on the following:
-
If the automatic registration read event is not enabled, reading will not continue after reading.
-
If the number of bytes read is smaller than the received cache, no data is available on the nic. Wait until the next read event is triggered to continue reading.
Step6: after one or more read operations are complete, a read completion event is triggered and propagated to the entire event chain.
The whole network read process is introduced here.
3. Accept the connection processing process
In Netty server receives the client connection requests (OP_ACCEPT), encapsulated in channelRead incident, its code entry for: AbstractNioMessageChannel inner classes NioMessageUnsafe read method.
The outline of its implementation has been described in the previous section. Here we will take a look at the doReadMessage of NioServerSocketChannel.
Accept a connection by using the underlying NIO and get the NioSocketChannel object. The object is then propagated down the channelRead event and manipulated in subsequent handlers, such as registering it for a read event that triggers a network read.