What is Node.js?

Node.js uses an event-driven, non-blocking I/O model, making it lightweight and efficient.

Node. js is a JavaScript runtime environment based on Chrome V8 engine. → How is running JavaScript in Node.js different from running JavaScript in Chrome? → Theoretically, there is almost no difference between writing JavaScript in Node.js and writing JavaScript in Chrome. → 1. Node.js has no browser API, i.e. Document, window, etc. 2. Added Node.jsAPI. → So for developers, writing JavaScript in Chrome to control the browser, Node.js lets us control the entire computer in a similar way.

Node.js non-blocking I/O

I/O is Input/Output, the Input and Output of a system. The difference between blocking and non-blocking I/O is whether the system can receive other inputs between input and output.

The key to understanding non-blocking I/O is to: 1) identify a system that does Input/Output, and 2) consider whether the system can do other Input/Output during the Input/Output process.

const glob = require('glob'); Var result = null; console.time('glob'); result = glob.sync(__dirname + '/**/*'); // Synchronously read the contents of the file console.timeEnd('glob'); // Prints the time when the file is read console.log(result); Glob: 12.77ms '/users/...... Var result = null; console.time('glob'); glob(__dirname + '/**/*', function(err,res){ result = res; //console.log(result); console.log('got result'); }) console.timeEnd('glob'); Cnosole. log(1 + 1) // Glob: 2.684ms 2 got resultCopy the code

Here is the schematic of asynchronous nodejs programming. We can think of the left side as a node.js thread. All node.js I/O operations are non-blocking, and it distributes a lot of computing power to other C++ threads (right side). Wait until the other C++ threads have finished calculating and then return the results to the node.js thread, which then returns the results to the application. Node.js threads are also processing other requests or events while other C++ threads are doing calculations, rather than waiting for the results of other C++ threads. This is the non-blocking I/O of Node.js.

Node.js event loop

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

Node.js implements a single-threaded, highly concurrent JavaScript runtime using event-driven and non-blocking I/O. Single threading means that only one thing can be done at a time. In general, the implementation of high concurrency is to provide a multi-threaded model, where the server allocates one thread to each client request. Using synchronous I/O, also known as blocking I/O, the system makes up for the time cost of synchronous I/O calls by switching threads. Node.js is a single-threaded model that uses a single main thread to handle all requests and then handles I/O operations asynchronously, avoiding the overhead and complexity of creating, destroying, and switching between threads.

Node.js maintains one in the main threadThe event queueWhen a request is received, it is placed in the queue as an event and continues to receive other requests. When the main thread is idle (when there are no requests for access), it starts to loop the event queue and check whether there are any events in the queue to be processed. There are two ways to do this: if it is a non-I /O task, it is handled by itself and returns to the upper level call through the callback function; If it is an I/O task, a thread is pulled from the thread pool to handle the event, the callback function is specified, and the rest of the queue continues to loop.

When the I/O task in the thread completes, the specified callback function is executed, and the completed event is placed at the end of the event queue, waiting for the event loop. When the main thread loops through the event again, it is processed directly and returned to the upper level call. This process is called an Event Loop

Var events = require('events'); // Create eventEmitter object var eventEmitter = new event.eventEmitter (); Var connectHandler = function connected() {console.log(' Connected successfully. '); // Emit the data_received eventEmitter. Emit ('data_received'); } // Bind connection event handler eventEmitter. On ('connection', connectHandler); // Bind data_Received eventEmitter. On (' data_Received ', function(){console.log(' Data received successfully. '); }); // Trigger the connection eventEmitter. Emit ('connection'); Console. log(" Program completed." ); // The connection is successful. Data is received successfully. Procedure The program is finished.Copy the code

Node.js asynchronous programming

The node.js callback function format is error-first callback/node-style callback.

The flow control problems that callback can cause: 1. The layers of asynchronous callbacks can easily create callback hell, making our code unmaintainable. 2, asynchronous event concurrency, need to obtain the results of two asynchronous events before the next step, then the code logic will be cumbersome

Async. Js (somewhat outdated) provides asynchronous concurrency control. Async. ForEachOf methods can execute each asynchronous operation separately and wait for the asynchronous operation to complete before executing the following logic.

var async = require("async"); var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"}; var configs = {}; async.forEachOf(obj, (value, key, callback) => { fs.readFile(__dirname + value, "utf8", (err, data) => { if (err) return callback(err); try { configs[key] = JSON.parse(data); } catch (e) { return callback(e); } callback(); });});}); }, err => { if (err) console.error(err.message); // configs is now a map of JSON data doSomethingWith(configs); });Copy the code

Promises can also be used for asynchronous programming in Node.js, and promises allow us to write asynchronous code in synchronous logic. A promise that executes then and catch returns a new promise whose final state is determined by the result of the execution of the callbacks to then and catch. At the same time, the results of concurrent asynchronous operations can be logically controlled by calling promise.all, promise.race and other APIS.

The ultimate solution to asynchronous programming: write async/await asynchronously. The async function is a syntactic sugar wrapper for promises, with async/await features:

  • The await keyword can “suspend” the execution of async functions
  • The await keyword gets the execution result of a promise in synchronous writing
  • A try-catch can get the result of an await

Node.js provides HTTP services

A web page request contains two HTTP packet exchanges: 1. The browser wants the HTTP server to send the request HTTP packet. 2. The HTTP server returns HTTP packets to the browser

The HTTP server provides THE HTTP service. The FUNCTION of the HTTP service is to parse HTTP request packets and return corresponding HTTP return packets.

var http = require("http");
 
http.createServer(function(req,res){
    res.writeHead(200);
    res.end('hello');
}).listen(3000)
Copy the code

Node.js introduces calls to the built-in HTTP module, which provides the createServer method to create an instance of the HTTP service and calls its LISTEN method to listen on port 3000 of the native host.

Node.js provides the Express framework – making it easier and simpler to write HTTP services, greatly reducing the development burden.

var fs = require("fs"); var express = require("express"); const app = express(); app.use(function(req, res){ }); Get ('/favicon.ico',funtion(req,res){res.status(200); // Get indicates the HTTP request by getting app.get('/favicon.ico',funtion(req,res){res.status(200); //res.writeHead(200); //res.end('hello'); }); // Return the local file app.get('/',funciton(req,res){res.send(fs.readfilesync (__dirname + '/index.heml',' utF-8 ')); }) / / request method is post app. The post ('/favicon. Ico, funtion (the req, res) {/ /... }); app.listen(3000);Copy the code

Express provides a middleware next method that implements the Onion model, but in asynchronous operations, the Onion model fails. Because Express support for asynchronous operations is not perfect, the Node.js community launched the Koa framework. Koa features:

  • Middleware implemented using async functions, with “pause execution”, also conforms to the Onion model in asynchronous cases
  • Context object to mount HTTP request and response, ctx.request, ctx.response. Body = fs.createreadStream (‘really_large.xml’); ctx.response.body = fs.createreadStream (‘really_large.xml’);
  • Compared to Express, Koa removes the routing function. Koa takes a minimalist approach and does not bind any middleware. We can implement routing in middleware (such as KOA-mount middleware), but there is no middleware for routing in Koa
var fs = require("fs"); var koa = require("koa"); var mount = require("koa-mount"); const app = koa(); app.use( mount('/favicon.ico',function(ctx){ ctx.status = 200; })); app.use( mount('/',function(ctx){ ctx.body = fs.readFileSync(__dirname + '/index.heml','utf-8'); })); app.listen(3000);Copy the code

Express VS Koa

  • Express has lower barriers, Koa is more powerful and elegant
  • Express encapsulates more, makes development easier, and Koa customizable

Remote Procedure Call (RPC) call

RPC call is similar to front-end Ajax in that Ajax refers to the communication between browser and server, while RPC call generally refers to the communication between server and another server. The similarities between RPC and Ajax are as follows: 1. Both are network communication between two computers; 2

The differences between RPC and Ajax are:

1. Ajax uses DNS as the siting service, but RPC does not necessarily use DNS as the addressing service (RPC communications are generally requests made on the Intranet, so using DNS as the siting service is a bit wasteful).

2. RPC application layer protocol generally does not use HTTP, and RPC communication generally uses binary protocol (smaller packet volume, faster codec rate, and superior performance).

3. RPC communication is based on TCP or UDP

Buffer

The JavaScript language itself only has string data types, not binary data types. But when dealing with things like TCP streams or file streams, you must use binary data. Buffer is a Buffer provided by Node. js that creates a dedicated Buffer for binary data. In Node.js, the Buffer class is the core library shipped with the Node kernel. The Buffer library gives Node.js a way to store raw data, allows Node.js to work with binary data, and is likely to be used whenever data needs to be moved during I/O operations in Node.js. The raw data is stored in an instance of the Buffer class. A Buffer is similar to an array of integers, but it corresponds to a block of raw memory outside of V8 heap memory.

Var buffer1 = buffer. from('Ashin'); Var buffer2 = Buffer. The from ([1, 2, 3, 4]); Var buffer3 = buffer. alloc(20); // write data to buffer buffer2.writeInt8(12,1); WriteInt16BE (512,2) writeInt16BE(512,2) writeInt16BE(512,2) writeInt16BE(512,2) In the same way, LE means the position is in front and the high position is behindCopy the code

Net Establishes RPC channels

Net provides services similar to HTTP in Node.js. We first use NET to create simmon communication between the client and the server: only the client can send data to the server, or only the server can send data to the client.

// const net = require('net'); const socket = new net.Socket({}); // Connect to socket. Connect ({host: '127.0.0.1', port: 4000}); socket.wirte("good morning"); // Write dataCopy the code

Net calls the createServer method to create a server that, unlike HTTP, receives the socket parameter, which represents a proxy object for writing and reading on the network path. Socket.on listens for data, and if the data changes, it executes the callback function immediately. The buffer in the callback function is the binary stream described above, and toString () returns a string.

//server.js const net = require('net'); const server = net.createServer((socket) => { socket.on('data', function (buffer) { console.log(buffer,buffer.toString()); })}); server.listen(4000);Copy the code

On this basis, to achieve half-duplex communication: the customer side sends the request, the server side receives the request and returns the data, that is, only one side is sending data at the same time. For example, the client sends a course ID to the server, and the server uses the course ID to return the corresponding course name to the client.

// const net = require('net'); const socket = new net.Socket({}); // Connect to socket. Connect ({host: '127.0.0.1', port: 4000}); const LESSON_IDS = [ "136797", "136798", "136799", "136800" ]; const buffer = Buffer.alloc(4); buffer.wirteInt32BE( LESSON_IDS[ Math.floor( Matn.random() * LESSON_IDS.lenth )] ) socket.wirte(buffer); // Write data socket.on('data', function (buffer) {console.log(buffer.tostring ()); }) //server.js const net = require('net'); const server = net.createServer((socket) => { socket.on('data', function (buffer) { const lessonId = buffer.readInt32BE(); setTimeout(()=>{ socket.write(Buffer.from(LESSON_DATA[lessonId])) },500) console.log(buffer,buffer.toString()); })}); server.listen(4000); / / false data const LESSON_DATA = {136797: "01 | course is an introduction," 136798:02 | content review, 136799: "03 | Node. Js?" 04, 136800: "| Node. Js can be used to do?" }Copy the code

In half-duplex communication, if the client sends multiple requests to the server in succession, the server returns two return packets after calculation. However, the sequence of return packets and request packets cannot be guaranteed. Therefore, on the basis of half-duplex communication, the serial number SEQ is added to each request, and the corresponding SEQ serial number is added to the returned message. In this way, after receiving multiple requests, the client can distinguish the corresponding requests of each returned message according to the serial number, thus realizing full-duplex communication.

// const net = require('net'); const socket = new net.Socket({}); Socket.connect ({host: '127.0.0.1', port: 4000}); const LESSON_IDS = [ "136797", "136798", "136799", "136800" ]; const buffer = Buffer.alloc(4); const id = Math.floor( Matn.random() * LESSON_IDS.lenth ); buffer.wirteInt32BE( LESSON_IDS[ id ] ) socket.wirte(buffer); // Write data socket.on('data', function (buffer) {console.log(buffer.tostring ()); }) let seq = 0; Function encode(index) {const seqBuffer = buffer.slice(0,2); cosnt titleBuffer = buffer.slice(2); console.log(seqBuffer.readreadInt32BE(), titleBuffer.toString()) const buffer = Buffer.alloc(6); buffer.writeInt16BE(seq++) buffer.writeInt32BE(LESSON_IDS[index], 2); return buffer; } socket.wirte(encode(id)); // Write data setInternal(()=>{id = math.floor (matn.random () * lesson_ids.lenth); socket.wirte(encode(id)); },50)// server. Js code const net = require('net'); const server = net.createServer((socket) => { socket.on('data', Function (buffer) {const seqBuffer = buffer.slice(0,2); const lessonId = buffer.readInt32BE(2); setTimeout(()=>{ const buffer = Buffer.concat([seqBuffer,Buffer.from(LESSON_DATA[lessonId])]); socket.write(buffer) }, 10 * Matn.random() ); // Returns data console.log(buffer, buffer.tostring ()) randomly within 1s; })}); server.listen(4000); / / false data const LESSON_DATA = {136797: "01 | course is an introduction," 136798:02 | content review, 136799: "03 | Node. Js?" 04, 136800: "| Node. Js can be used to do?" }Copy the code