Search code examples
javascriptnode.jsmultithreadingasynchronoussingle-threaded

Requests handling inside Event Pool using NodeJS


I have read the difference between Multi thread mechanism and NodeJS Single thread mechanism here. I know less about thread concept.

My question

The above article says that all the Non Blocking I/O is handled using single thread in Event loop.

I have read through questions posted in this forum, but all it says is just the overview of how single thread is working and not the deeper mechanism. says something like... Starts processing the Client Request If that Client Request Does Not requires any Blocking IO Operations, then process everything, prepare response and send it back to client.

If there are like 2 or more Non Blocking requests in Event Queue, Event loop takes each requests and processes it.

First request enter Event Pool and starts processing and does not wait or hold till the response and meanwhile request 2 enters and starts processing without wait.

Now,since the 2nd request has taken the thread for processing (and all request is handled using single thread) , currently what is handling the 1st request process, If there is thread sharing , how is it happening ?

Is the first request process released when handling 2nd request and later comes back to 1st request ? if so how is it happening in thread perspective ?

how does single thread processes 2 or more request concurrently as basically thread will be assigned to a request until all it's process is finished

and how is single thread handled for both Input and Output operation at same time ?

is there any topic i am missing to read so that i'm getting this single thread event loop mechanism ?


Solution

  • First off, "single threaded" applies only to one thread running your Javascript. The node.js has other native threads for implementing some of the functions in the built-in library. For example, file I/O uses a thread pool in order to implement asynchronous file I/O. But, what's most important to understanding how your own Javascript runs is that there is only one thread of your Javascript.

    Let's imagine that you have a simple web server like this:

    const http = require('http');
    
    function sendFile(res, filename) {
        if (filename.startsWith("/")) {
            filename = filename.slice(1) + ".html";
        }
        fs.readFile("1.html", (err, data) => {
           if (err) {
               res.writeHead(404);
               res.end("not found");
           } else {
               res.writeHead(200, {'Content-Type': 'text/html'});
               res.write(data);
               res.end();
           }
        });
    
    }
    
    const server = http.createServer((req, res) => {
        if (req.url === "/1" || req.url === "/2" || req.url === "/3") {
            sendFile(req.url);
        } else {
            res.writeHead(404);
            res.end("not found");
        }
    });
    
    server.listen(80);
    

    This web server responds to requests for three URLs /1, /2 and /3.

    Now imagine that three separate clients each request one of those URLs. Here's the sequence of events:

    1. Client A requests http://myserver.com/1
    2. Client B requests http://myserver.com/2
    3. Client C requests http://myserver.com/3
    4. Server receives incoming connection from client A, establishes the connection, client sends the request for /1 and the server reads and parses that request.
    5. While the server is busy reading the request from client A, the requests from both client B and client C arrive.
    6. The TCP stack handles incoming connections at the OS level (using other threads i.e. kernel level thread).
    7. Notifications of the arriving connections are put in the node.js event queue. Because the node.js server is busy running Javascript for the client A connection, those two events sit in the event queue for now.
    8. At the same time as those other connections are arriving, the node.js server is starting to run the request handler for /1. It finds a match in the first if statement and calls sendFile("/1").
    9. sendFile() calls fs.readFile() and then returns. Because fs.readFile() is asynchronous, that file operation is started, but is handed over to the I/O system inside of node.js and then the function immediately returns. When sendFile() returns, it goes back to the http server request handler which also then returns. At this point, there's nothing else for this request to do. Control has been returned back to the interpreter to decide what to do next.
    10. The node.js interpreter checks the event queue to see if there is anything in their to process. It finds the incoming request from client B and that request starts processing. This request goes through the same 8 and 9 steps until it returns with another fs.readFile() operations initiated.
    11. Then step 10 is repeated for the incoming request from client C.
    12. Then, some short time later, one of the three fs.readfile() operations that were previously initiated completes and places a completion callback into the Javascript event queue. As soon as the Javascript interpreter has nothing else to do, it finds that event in the event queue and begins to process it. This calls the callback that was passed to fs.readFile() with the two parameters that that function expects and the code in the callback starts to execute.
    13. Assuming the fs.readFile() operation was successful, it calls res.writeHead(), then res.write(), then res.send(). Those three calls all send data to the underlying OS TCP stack where it is then sent back to the client.
    14. After res.end() returns, control is returned back to the interpreter and it checks the event queue for the next event. If another fs.readFile() callback is already in the event queue, then it is pulled out of the event queue and processed like the previous one. If the event queue is empty, then the interpreter waits until something is put in the event queue.

    If there are like 2 or more Non Blocking requests in Event Queue, Event loop takes each requests and processes it.

    node.js only runs one at a time. But, the key is that asynchronous code in the request handler allows the handler to return control back to the system so that other events can be processed while that first request was waiting for its asynchronous operation to complete. This is a form of cooperative, non-pre-emptive multi-tasking. It's not multiple threads of Javascript. The first request handler actually starts and asynchronous operation and then returns (as if it was done). When it returns, the next event in the queue can start processing. At some later time when the asynchronous operation completes, it will insert its own event into the event queue and it will get back in line to use the single thread of Javascript again.

    First request enter Event Pool and starts processing and does not wait or hold till the response and meanwhile request 2 enters and starts processing without wait.

    Most of this has already been described above. If the Javascript thread is busy why request 2 enters the event queue, that request will sit in the event queue until the Javascript thread is no longer busy. It may have to wait a short period of time. But, it won't have to wait until request 1 is done, only until request 1 returns control back to the system and is, itself, waiting for some asynchronous operation to complete.

    Now,since the 2nd request has taken the thread for processing (and all request is handled using single thread) , currently what is handling the 1st request process, If there is thread sharing , how is it happening ?

    While the 2nd request is using the Javascript thread, the 1st request is not running any Javascript. It's native code asynchronous operations may be running in the background (all asynchronous operations require some native code), but there is only one piece of Javascript running at any given time so if the 2nd request is running some Javascript, then the first request is either waiting for its asynchronous operation to finish or that operation has already finished and an event is sitting in the event queue waiting for the 2nd request to be done so that event can get processed.

    Is the first request process released when handling 2nd request and later comes back to 1st request ? if so how is it happening in thread perspective ?

    This all works through the event queue. 1st request runs until it returns. 2nd request runs until it returns. When async operation from 1st request completes it inserts an item in the event queue. When the JS interpreter is free, it pulls that event from the event queue and runs it. There may be threads involved in native code implementations of asynchronous operations, but there is still only one thread of Javascript.

    how does single thread processes 2 or more request concurrently as basically thread will be assigned to a request until all it's process is finished

    It never actually runs multiple pieces of Javascript concurrently. The Javascript from each different operation runs until it returns control back to the interpreter. Asynchronous operations (such as file I/O or networking operations) can run concurrently and those are managed by native code, sometimes using additional threads and sometimes not. File I/O uses a thread pool to implement non-blocking, asynchronous file I/O. Networking uses OS event notifications (select, epoll, etc...), not threads.

    and how is single thread handled for both Input and Output operation at same time ?

    It doesn't in your Javascript. It would typically read, then write. It doesn't do both "at the same time". Now, the TCP stack may be doing some actual parallel work inside the OS, but that's all managed by the OS and even that probably gets serialized at the network interface at some point.Requests are handled by single thread where as input output processes are managed by os level threads created per each process by OS

    is there any topic i am missing to read so that i'm getting this single thread event loop mechanism ?

    Read every thing you can find about the Javascript event queue. Here are some references to get you started:

    How does JavaScript handle AJAX responses in the background?

    Where is the node.js event queue?

    Node.js server with multiple concurrent requests, how does it work?

    Asynchronous process handler in node

    How does a single thread handle asynchronous code in Node.js?