Recently, I have been reading the book “Nodejs in Plain English”. The purpose of writing this article is to record those node knowledge points that I think are useful.
Introduction of the node
Node is an event-driven server that implements non-blocking I/O. The Node architecture is similar to Chrome’s in that it is an asynchronous, event-driven architecture where browsers interact with each other and Node serves I/O with the event driver.
Node Application Scenarios
Because Node is asynchronous I/O, it is natural to handle I/ O-intensive tasks (file operations, network communications, etc.). Node takes advantage of the processing power of event loops, rather than starting each thread to service each request, with minimal resource usage. CPU intensive computing tasks are largely due to the deep optimization of the V8 engine.
Why is Node single threaded?
The biggest benefit of single-threading is that you don’t have to worry about state synchronization as much as you do with multithreaded programming, there are no deadlocks, and there are no performance costs associated with thread context switching.
Disadvantages of single threading?
- Unable to utilize multi-core CPUS
- An error can cause an entire application to quit, and the robustness of the application is worth testing
- A large number of calculations occupy the CPU, causing asynchronous I/O calls to fail
Just as the BROWSER JS and UI share the same thread, the Node CPU will be occupied for a long time. As a result, subsequent asynchronous I/O cannot be called and completed asynchronous I/O functions cannot be executed in a timely manner. Node has a child process called child_process. When a large number of computational tasks are encountered, the child process can be decomposed into each process, and then pass the results through inter-process communication.
Asynchronous I/O
Asynchronous I/O and non-blocking I/O
Asynchronous and non-blocking may sound like the same thing, but in practical terms both serve parallel purposes. But as far as the computer kernel is concerned, synchronous/asynchronous and blocking/non-blocking are really two different things. The operating system has only two ways of dealing with I/O, blocking and non-blocking. Blocking I/ OS Causes the CPU to wait for I/ OS. As a result, the CPU processing capability is not fully utilized. After the non-blocking I/O is returned, the CPU can process other transactions, but because the I/O is not completed, the data is not the data we want. In order to obtain the complete data, the application needs to repeatedly call the I/O to confirm whether it is complete. This technique of repeated call judgment is called polling.
Asynchronous I/O in Node — event loop
When a Node process starts, there is a loop similar to while(true), and each time the loop body is executed, we call it tick. The process of each tick is to check if there are any events that need to be handled, and if there are, pull out the event and its associated callback function. If there are related callback functions, execute them. It then enters the next loop and exits the process if there is no more event handling.
Asynchronous I/O in Node — observer
In the process of each tick, each event loop has one or more observers, and the process of determining whether there are events to be processed is to ask these observers. In Node, events mainly come from network requests and file operations. Observers of these events include network observers and file I/O observers. Observers classify events.
How is asynchronous I/O implemented in NodeJS? How is asynchrony different from asynchrony in browsers
Asynchronous I/O in NodeJS has the participation of I/O thread pools. Node starts with an event loop in which an I/O observer watches to see if any I/O calls end. When a node initiates an asynchronous I/O operation, it encapsulates a request object, encapsulates the parameters of the I/O operation and the callback function into the object, and then passes the object to the I/O thread pool. The I/O thread pool receives the object and determines whether the thread is available. If so, it performs the I/O operation in the object. The result of execution is also placed in the request object, and the I/O observer is notified that the call is complete, at which point the thread pool returns to the available state. The I/O observer can take the request object, retrieve the return function and execution result from the request object, and execute the callback function. An asynchronous I/O operation is complete.
Non-i /O asynchronous apis
There are also NON-I /O related apis in Node. These apis are setTimeout, setInterval, setImmediate, Process.Nexttick, and setImmediate. SetTimeout and setInterval are the same as in browsers, but in contrast to asynchronous I/O, there is no I/O thread pool involved. Each timer created by setTimeout and setInterval will be inserted into a red-black tree inside the timer observer. Each time the Tick is executed, the timer object will be removed from the red-black tree to check whether the time is exceeded. If the time is exceeded, an event will be generated and the callback function will be executed immediately. Each time the process.nexttick () method is called, the callback function is simply queued up for execution on the nextTick. Compared to setTimeout and setInterval, process.nexttick () is more efficient because timers require operations such as drawing on red-black trees, creating timer objects, and iterating. SetImmediate is similar to process.nexttick () in that the callback is delayed, but there are some subtle differences. The callback function in process.nexttick () takes precedence over setImmediate. The reason is that the loop of events checks the observer sequentially. Process.nexttick () belongs to the idle observer, and setImmediate belongs to the check observer. In each cycle check, the IDLE observer precedes the I/O observer, and the I/O observer precedes the check observer. The callbacks to process.nexttick () are stored in an array, and setImmediate’s are stored in a linked list. Process.nexttick () executes all of the callbacks in the array in each loop, and setImmediate executes one of the callbacks in the list in each loop.
Module mechanism Commonjs
The module definition
The context provides the ·require method to import external modules, and provides exports objects to export methods or variables that are unique to the export. In modules, there is also a Module object that represents the module itself, and exports is an attribute of module. A file is a module. Node modules are divided into two types. One is the module provided by Node, which is called core module. The other is user-written modules called file modules.
Meaning of a module definition
Restrict clustering methods or variables to private scopes, while supporting import and export functions to smoothly connect upstream and downstream dependencies. The Commonjs export – in mechanism eliminates the need for variable contamination
Introduces the steps the module goes through
- Path analysis
- File location
- Compile implementation
- Core modules are compiled into binary executables during compilation of node source code. When the Node process starts, the core module is directly loaded into memory, so the file location and compilation execution of the two steps are directly omitted, and the path analysis priority judgment, so the loading speed is the fastest.
- File modules are loaded dynamically at run time, requiring three completed processes, so loading is slower.
Browsers cache static file scripts to improve performance, while Node caches compiled objects to improve performance. When require() is introduced, the same module is loaded from the cache first, which is the first priority, but the cache check for the core module precedes the file module. Optimization policies loaded from the cache can greatly improve efficiency by eliminating the need for path analysis, file location, and compilation execution during secondary import.
Modules compiled
- The.js file is synchronously read by the FS module and then compiled and executed
- The.node file is now an extension file written in C/C ++, loaded with dlopen() and then compiled
- Json file is read synchronously by fs module, and json. parse is used to parse the returned result.
Compilation of javascript modules
During js compilation, Node wraps the contents of the retrieved JS file. That is, the original JS code with a function wrapped up.
(function(exports.require.module,__filename,__dirname){
var Math = require('math');
exports.area = function(radius){
return Math.PI*radius
}
});
Copy the code
Memory control
Garbage collection mechanism
V8 objects can be divided into new generation objects (short lifetime) and old generation objects (long lifetime). The new generation object adopts the way of copying and moving to realize garbage collection. The memory space of the new generation is divided into two parts, one is From space, the other is to space. Object allocation is performed in the From space first. When garbage collection is performed, surviving objects are copied To the To space, then all objects From the From space are emptied, then objects From the To space are copied To the From space, and finally the To space is emptied. When the new generation memory garbage collection is performed, the object is checked to see if it has already undergone a garbage collection, and if so, the object is upgraded to the old generation object. At the same time, the To space is checked To see if the memory usage exceeds 25%, and if so, the remaining objects are set To old generation objects. The old generation object adopts the mark elimination method, which traverses all the objects in the heap in the mark phase and marks the living objects. In the subsequent clear phase, only the unmarked objects are cleared. One of the problems with mark cleaning is that after a round of garbage collection, the memory space will be discontinuous. This kind of memory fragmentation will cause problems for subsequent memory allocation. Therefore, the mark cleaning method appears.