nodejs asynchronous architecture

One of the cores of nodejs is non-blocking asynchronous IO, so I wanted to know how it is implemented. I dug into the nodejs source code and found some answers. I will share them with you here. First, I used a piece of js code test-fs-read.js for testing. The code is as follows:

var path = require('path'),
 fs = require('fs'),
 filepath = path.join(__dirname, 'experiment.log'),
 fd = fs.openSync(filepath, 'r');
 
 fs.read(fd, 12*1024*1024, 0, 'utf-8', function(err, str, bytesRead) {
     console.log('[main thread] execute read callback');
 });
 console.log('[main thread] execute operation after read');


The asynchronous IO operation of this code is on the call of fs.read. The experiment.log read is a 12M text file. The so-called asynchronous, you can probably imagine that when running, it will print [main thread first ] execute operation after read and then print [main thread] in the callback function execute read callback
But you may have heard that nodejs is single-threaded, so how does it implement asynchronous IO? ? Where is the file reading operation performed? How do you call the callback function after reading? I guess that reading the file may be completed in another thread. After reading, nodejs is notified through the event to execute the callback. In order to find out, I debugged the source code of nodejs and libeio. After recompiling, I ran the test code node test-fs-read.js. The output is as follows:
 
You can see the IO operation of nodejs It is completed by calling the libeio library. Debugging starts from fs.read. After the js code is compiled with v8, fs.read will call the Read method in node_file.cc. The running of the test code goes through the following steps:
1 The Read method in node_file.cc calls eio_read of libeio (eio.c), and the read request is placed in the request queue req_queue.
2 The main thread creates an eio thread, and the read call of the main thread returns at this time.
3 The eio thread takes out a request from req_queue and starts executing read IO
4 The main thread continues to perform other operations after the read call.
5 The main thread polls eio and retrieves the completed request from the response queue res_queue. At this time, res_queue is empty and the main thread stops poll
6 The eio thread completes the read IO, read requests are put into the response queue res_queue, and the libev event want_poll is sent to the main thread (through the callback function provided when the main thread initializes eio).
7 The eio thread takes a request from req_queue, and there are no requests at this time.
8 The main thread responds to the want_poll event and takes out a request from the res_queue. After taking out the request, the res_queue becomes empty and the main thread sends the done_poll event.
9 The main thread executes the requested callback function.


It should also be noted that when there are multiple IO requests at the same time, the main thread will create multiple eio threads to increase the processing speed of IO requests. In order to see the IO execution process of nodejs more clearly, the diagram is as follows. The sequence number is only used to mark the process and has no correspondence with the sequence number of the above steps. Finally, I would like to summarize a few points. Please correct me if I am inappropriate. 1 nodejs obtains the IO execution status through libev events instead of polling, which improves CPU utilization. 2 Although nodejs is single-threaded, its IO operations are multi-threaded. Multiple IO requests will create multiple libeio threads (up to 4), which improves the performance of IO operations under normal circumstances. 3 However, when the IO operation situation is relatively complex, it may cause a thread competition state, resulting in reduced IO performance; and libeio can create up to 4 threads. When there are a large number of IO requests at the same time, the actual performance needs to be measured. In addition, since each IO request corresponds to a libeio data structure, when a large number of IO operations reside in the system at the same time, memory overhead will increase. 4 Libeio brings additional management to implement asynchronous IO functions. When the amount of IO data is relatively small, the overall performance is not necessarily better than synchronous IO.​​     

Guess you like

Origin blog.csdn.net/weixin_39896629/article/details/134346382