Asynchronous resource context sharing means sharing context data within a network request life cycle or asynchronous resource call chain.
Before answering this question, we must first understand what asynchronous resources are.
Asynchronous resources can be understood as objects with callbacks, such as but not limited to Promises, Timeouts, TCPWrap, UDP, etc. See the list of asynchronous resource types for details.
The official definition is as follows:
An asynchronous resource represents an object with an associated callback. This callback may be called multiple times, such as the 'connection' event in net.createServer(), or just a single time like in fs.open(). A resource can also be closed before the callback is called.
Here we introduce AsyncLocalStorage, the asynchronous context sharing solution officially provided by Node.js. This feature was still an experimental feature before 16.4.0 and has been stable since 16.4.0.
AsyncLocalStorage can share data in a chain of asynchronous operations.
AsyncLocalStorage instance asyncLocalStorage has the following main methods:
example:
const store = { id: 1 }; // Replaces previous store with the given store object asyncLocalStorage.enterWith(store); asyncLocalStorage.getStore(); // Returns the store object someAsyncOperation(() => { asyncLocalStorage.getStore(); // Returns the same object });
The first parameter of the asyncLocalStorage.run() function is to store the shared data we need to access in the asynchronous call, and the second parameter is an asynchronous function.
The following is an example to demonstrate how to use AsyncLocalStorage to implement asynchronous resource context sharing:
Output:
runA 8f19ebef-58d7-4b1a-8b9b-46d158beb5d2 2022/5/24 20:26:17 this is a log message runB 8f19ebef-58d7-4b1a-8b9b-46d158beb5d2 2022/5/24 20:26:17 this is a log message
In the same asynchronous function run through asyncLocalStorage.run, functions runA and function runB will be run, and runA and runB can be accessed to the same context data.
AsyncLocalStorage provides a great traversal for us to easily implement asynchronous resource context sharing in Node.js, but every asynchronous resource operation will trigger Async Hooks, which will inevitably have a certain impact on the performance of our Node application. So how big is the impact?
According to an actual measurement by Kuzzle, using AsyncLocalStorage will cause about 8% additional performance loss. Of course, different business scenarios may have different performance. If you are concerned about this part of performance, you can also add comparative testing to your business to test the specific performance impact.
---- | Log with AsyncLocalStorage | Log classic | difference |
---|---|---|---|
req/s | 2613 | 2842 | ~8% |
In other multi-threaded languages, each HTTP creates a new thread, and each thread has its own memory. You can store global state in thread memory and retrieve global state from anywhere in your code.
In Node.js, because Node.js is single-threaded and shares memory among all HTTP requests, each HTTP request cannot hold global state that is isolated from each other.
AsyncLocalStorage can effectively isolate the status between different asynchronous operations, and plays a very important role in scenarios such as HTTP request tracking, APM tools, context log tracking, and request-based full-link log tracking.