面试题答案
一键面试1. 引入 async_hooks
模块
在 Node.js 项目中,首先需要引入 async_hooks
模块:
const asyncHooks = require('async_hooks');
2. 创建 AsyncHook
实例并注册回调函数
AsyncHook
类提供了跟踪异步资源生命周期的方法,我们需要创建它的实例并注册相关回调函数。对于数据库查询这种异步操作,重点关注 init
和 destroy
回调。
const asyncHook = asyncHooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (type === 'DbQuery') { // 假设数据库查询类型标识为 'DbQuery'
// 在这里可以开始记录上下文信息,例如记录开始时间
const startTime = Date.now();
// 可以将相关信息存储在一个 Map 中,以 asyncId 作为键
contextMap.set(asyncId, { startTime });
}
},
destroy(asyncId) {
if (contextMap.has(asyncId)) {
const context = contextMap.get(asyncId);
const endTime = Date.now();
const duration = endTime - context.startTime;
// 可以在这里记录性能信息,例如打印数据库查询耗时
console.log(`DbQuery with asyncId ${asyncId} took ${duration} ms`);
contextMap.delete(asyncId);
}
}
});
3. 激活 AsyncHook
创建实例并注册回调后,需要激活 AsyncHook
才能开始跟踪:
asyncHook.enable();
4. 可能涉及的代码结构
- 全局上下文管理:可以使用一个全局的
Map
来存储每个异步操作的上下文信息,例如上述代码中的contextMap
:
const contextMap = new Map();
- 封装数据库查询操作:在项目中,将数据库查询操作封装起来,并在封装函数内部标记该异步操作为
DbQuery
类型。例如,假设使用mysql2
库进行数据库查询:
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'test',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
function queryDatabase(sql, values) {
return new Promise((resolve, reject) => {
const asyncResource = new asyncHooks.AsyncResource('DbQuery');
pool.getConnection((err, connection) => {
if (err) {
asyncResource.emitDestroy();
return reject(err);
}
connection.query(sql, values, (queryErr, results) => {
connection.release();
if (queryErr) {
asyncResource.emitDestroy();
return reject(queryErr);
}
asyncResource.emitDestroy();
resolve(results);
});
});
});
}
通过以上步骤,利用 Async Hooks
就可以对特定类型(如数据库查询)的异步操作进行上下文跟踪,从而更好地排查性能问题和错误。