Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use the Async Hooks module of Node.js to track asynchronous resources

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report