The cornerstone behind NiO design: the reactor model, which is used for event Multi-Channel Separation and dispatching of the architecture model.
Reactor: an architecture pattern used for event Multi-Channel Separation and allocation
Normally, a file or device specified by a file descriptor works in two ways: Blocking
Non-blocking
. The so-called blocking means that when you try to read and write the file descriptor, if there is nothing to read at the time, or it is temporarily unavailable, the program will enter the waiting state.
Status until something is readable or writable. For non-blocking states, if nothing is readable or not writable, the read/write function will return immediately without waiting.
.
A common practice is: each time a socket connection is established
Create a new thread to communicate with the socket separately (blocking communication is adopted ). This method has a high response speed and is easy to control. It is very effective when the number of connections is small,
If a thread is generated for each connection, it is undoubtedly a waste of system resources. If there are many connections, there will be insufficient resources.
Another efficient method is to store a socket connection on the server.
And then Round-Robin the list. If a socket port is read (read-only), the corresponding read operation of the socket connection is called.
When data is writable on the socket port (write-ready), the corresponding write operation of the socket connection is called. If the socket connection of a port is interrupted, the corresponding destructor is called to close it.
This port. This greatly improves the efficiency of fully utilizing server resources.
In the traditional blocking Io mode, each connection must be handled by a thread, and the thread cannot exit.
Non-blocking I/O. Because it is based on the reactor mode, it is used in the architecture mode of event Multi-Channel Separation and allocation, so it can be processed by the thread pool. When the event comes, it is processed, and the thread is returned after processing. While
Traditional blocking methods cannot be processed using thread pools. If there are currently 10000 connections, the non-blocking mode may be done using the thread pool of 1000 threads, the traditional blocking method requires 10000
. If the number of connections is large, there will be insufficient resources. The core advantage of non-blocking is here.
Why is this happening? We will analyze them in detail below:
First, let's analyze the bottleneck of traditional blocking Io. When the number of connections is small, traditional I/O writing is easy to use. However, as the number of connections increases, traditional I/O problems will not work. Because
As mentioned above, traditional Io processes consume every connection
One thread, and the program's efficiency increases with the increase of the number of threads when the number of threads is small, but after a certain number, it decreases with the increase of the number of threads. Here we come to the conclusion that the traditional blocking Io
The bottleneck is that you cannot process too many connections.
Then, the purpose of non-blocking Io is to solve this bottleneck. How is non-blocking I/O implemented? The number of threads for non-blocking I/O processing connections is not linked to the number of connections, that is, processing
10000 non-blocking Io connections do not require 10000 threads. You can use 1000 or 2000 threads for processing. Because non-blocking Io processing connections are asynchronous. When a connection sends
Request to the server. The server regards this connection request as a request "Event" and assigns this "Event" to the corresponding function for processing. We can put this processing function in the thread for execution.
Thread return. In this way, a thread can process multiple events asynchronously. Most of the time for blocking I/O threads is wasted waiting for requests.