In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
Editor to share with you how to use Node.js 's Async Hooks module to track asynchronous resources. I hope you will get something after reading this article. Let's discuss it together.
The Async Hooks function is a new core module added to the Node.js v8.x version. It provides API to track the declaration cycle of asynchronous resources in Node.js programs and to share data among multiple asynchronous calls.
ExecutionAsyncId and triggerAsyncId
The async hooks module provides an asynchronous resource Id that marks the current execution context with the executionAsyncId () function, which is expressed in asyncId below. There is also a triggerAsyncId () function to identify the asynchronous resource Id from which the current execution context is triggered, that is, by which asynchronous resource the current asynchronous resource is created. Each asynchronous resource generates an asyncId, which is generated incrementally and is globally unique in the current instance of Node.js.
Const asyncHooks = require ('async_hooks'); const fs = require (' fs'); const asyncId = () = > asyncHooks.executionAsyncId (); const triggerAsyncId = () = > asyncHooks.triggerAsyncId (); console.log (`Global asyncId: ${asyncHooks.executionAsyncId ()}, Global triggerAsyncId: ${triggerAsyncId ()} `); fs.open ('hello.txt', (err, res) = > {console.log (`fs.open asyncId: ${asyncId ()}, fs.open triggerAsyncId: ${triggerAsyncId ()}`);})
The following is the result of our run. The global asyncId is 1 and the triggerAsyncId printed in the callback is 1 triggered globally.
Global asyncId: 1, Global triggerAsyncId: 0 fs.open asyncId: 5, fs.open triggerAsyncId: 1
Promise execution trace that is not enabled by default
By default, there is no asyncId assigned to Promise execution due to the relative performance consumption of promise introspection API provided by V8. This means that by default, programs that use Promise or Async/Await will not correctly execute and trigger the ID of the Promise callback context. Neither the current asynchronous resource asyncId nor the triggerAsyncId created by the current asynchronous resource can be obtained, as shown below:
Promise.resolve () .then (() = > {/ / Promise asyncId: 0. Promise triggerAsyncId: 0 console.log (`Promise asyncId: ${asyncId ()}. Promise triggerAsyncId: ${triggerAsyncId ()} `);})
Create a hooks object through asyncHooks.createHook to enable Promise asynchronous tracing.
Const hooks = asyncHooks.createHook ({}); hooks.enable (); Promise.resolve (). Then () = > {/ / Promise asyncId: 7. Promise triggerAsyncId: 6 console.log (`Promise asyncId: ${asyncId ()}. Promise triggerAsyncId: ${triggerAsyncId ()} `);})
Life cycle of asynchronous resources
The createHook () method of asyncHooks returns an instance of enabling (enable) and disabling (disable) hooks, which receives four callbacks from init/before/after/destory to indicate that an asynchronous resource destroys the entire lifecycle process from initialization, before callback call, after callback call.
Init (initialization)
Called when a class is constructed that may emit asynchronous events.
Async: unique id for asynchronous resources
Type: asynchronous resource type, corresponding to the constructor name of the resource. For more types, refer to async_hooks_type.
TriggerAsyncId: the asynchronous resource id created by which asynchronous resource is the current asynchronous resource
Resource: initialized asynchronous resource
/ * Called when a class is constructed that has the possibility to emit an asynchronous event. * @ param asyncId a unique ID for the async resource * @ param type the type of the async resource * @ param triggerAsyncId the unique ID of the async resource in whose execution context this async resource was created * @ param resource reference to the resource representing the async operation, needs to be released during destroy * / init? (asyncId: number, type: string, triggerAsyncId: number, resource: object): void
Before (before callback function call)
When you start an asynchronous operation (such as a TCP server receiving a new link) or complete an asynchronous operation (such as writing data to disk), the system will call a callback to notify the user, that is, the business callback function we wrote. The before callback will be triggered before this.
/ * When an asynchronous operation is initiated or completes a callback is called to notify the user. * The before callback is called just before said callback is executed. * @ param asyncId the unique identifier assigned to the resource about to execute the callback. * / before? (asyncId: number): void
After (after callback function is called)
The after callback is triggered after the callback processing is completed. If an uncaught exception occurs in the callback, the after callback is triggered after the uncaughtException event or domain processing is triggered.
/ * Called immediately after the callback specified in before is completed. * @ param asyncId the unique identifier assigned to the resource which has executed the callback. * / after? (asyncId: number): void
Destory (destroy)
Call destroy callback when the asynchronous resource corresponding to asyncId is destroyed. The destruction of some resources depends on garbage collection, so if there is a reference to the resource object passed to the init callback, destory may never be called, resulting in a memory leak in the application. This will not be a problem if the resource does not rely on garbage collection.
/ * * Called after the resource corresponding to asyncId is destroyed * @ param asyncId a unique ID for the async resource * / destroy? (asyncId: number): void
PromiseResolve
The promiseResolve callback is triggered when the resolve () function passed to the Promise constructor executes.
/ * Called when a promise has resolve () called. This may not be in the same execution id * as the promise itself. * @ param asyncId the unique id for the promise that was resolve () d. * / promiseResolve? (asyncId: number): void
The following code triggers two promiseResolve () callbacks, the first is the resolve () function that we called directly, and the second is in .then () although we don't have the call shown, it also returns a Promise so it will be called again.
Const hooks = asyncHooks.createHook ({promiseResolve (asyncId) {syncLog ('promiseResolve:', asyncId);}}); new Promise ((resolve) = > resolve (true)). Then ((a) = > {}); / / output result promiseResolve: 2 promiseResolve: 3
Note that logging in init callback causes "stack overflow" problem.
The first phase of the init callback in the life cycle of an asynchronous resource is called when you construct a class that may issue asynchronous events. Note that since using console.log () to output logs to the console is an asynchronous operation, using a similar asynchronous operation in the AsyncHooks callback function will trigger the init callback function again, resulting in an infinite recursive RangeError: Maximum call stack size exceeded error, that is, "stack overflow".
When debugging, a simple way to log is to use fs.writeFileSync () to write to the log synchronously, which will not trigger AsyncHooks's init callback function.
Const syncLog = (... args) = > fs.writeFileSync ('log.txt', `${util.format (... args)}\ n`, {flag:' a'}); const hooks = asyncHooks.createHook ({init (asyncId, type, triggerAsyncId, resource) {syncLog ('init:', asyncId, type, triggerAsyncId)}); hooks.enable () Fs.open ('hello.txt', (err, res) = > {syncLog (`fs.open asyncId: ${asyncId ()}, fs.open triggerAsyncId: ${triggerAsyncId ()} `);})
If you output the following, the init callback will only be called once, because the fs.writeFileSync is synchronized and the hooks callback will not be triggered.
Init: 2 FSREQCALLBACK 1 fs.open asyncId: 2, fs.open triggerAsyncId: 1
Sharing context between asynchrony
Node.js v13.10.0 adds the AsyncLocalStorage class of the async_hooks module, which can be used to share data among a series of asynchronous calls.
As shown in the following example, the first parameter of the asyncLocalStorage.run () function is to store the shared data we need to access in the asynchronous call, the second parameter is an asynchronous function, and we call the test2 function in the callback function of setTimeout (). This series of asynchronous operations do not affect us to get the shared data stored in the asyncLocalStorage.run () function where we need it.
Const {AsyncLocalStorage} = require ('async_hooks'); const asyncLocalStorage = new AsyncLocalStorage (); asyncLocalStorage.run ({traceId: 1}, test1); async function test1 () {setTimeout (() = > test2 (), 2000);} async function test2 () {console.log (asyncLocalStorage.getStore () .traceId);}
AsyncLocalStorage has many uses, such as log analysis, which is essential on the server side. If an HTTP log output from request to response to the whole system interaction can be associated with a traceId, the entire call link can be clearly seen when analyzing the log.
The following is a simple example of a HTTP request that simulates asynchronous processing and tracks the stored id during log output
Const http = require ('http'); const {AsyncLocalStorage} = require (' async_hooks'); const asyncLocalStorage = new AsyncLocalStorage (); function logWithId (msg) {const id = asyncLocalStorage.getStore (); console.log (`${id! = = undefined? Id:'-'}: `, msg);} let idSeq = 0; http.createServer ((req, res) = > {asyncLocalStorage.run (idSeq++, ()) = > {logWithId ('start'); setImmediate () = > {logWithId (' processing...'); setTimeout (() = > {logWithId ('finish'); res.end ();}, 2000)});})
Here is the result of the run. I called the second time directly after the first call, and you can see that the id information we stored was successfully printed out along with our log.
After reading this article, I believe you have some understanding of "how to use Node.js 's Async Hooks module to track asynchronous resources". If you want to know more about it, you are welcome to follow the industry information channel. Thank you for reading!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.