ردیابی کانتکست‌های ناهمگام در Node.js

خلاصه ماژول node:async_hooks در Node.js ابزارهایی برای ردیابی و مدیریت کانتکست‌های ناهمگام فراهم می‌کند. دو کلاس اصلی این ماژول، یعنی AsyncLocalStorage و AsyncResource

AsyncLocalStorageAsyncResourceContext Propagationasync_hooksWorker PoolEventEmitter Integration

~3 دقیقه مطالعه • بروزرسانی ۵ دی ۱۴۰۴

1. AsyncLocalStorage


کلاس AsyncLocalStorage یک فضای ذخیره‌سازی ایجاد می‌کند که در طول عملیات ناهمگام پایدار باقی می‌ماند. این کلاس برای ساخت loggerها، مدیریت sessionها یا انتقال داده در زنجیره‌های Promise بسیار مفید است.

مثال ساده:

const { AsyncLocalStorage } = require('node:async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();

function logWithId(msg) {
  const id = asyncLocalStorage.getStore();
  console.log(`${id !== undefined ? id : '-'}:`, msg);
}

هر نمونهٔ AsyncLocalStorage مستقل است و می‌تواند بدون تداخل با نمونه‌های دیگر استفاده شود.

2. متدهای مهم AsyncLocalStorage


  • run(store, callback): اجرای یک تابع در کانتکست مشخص
  • getStore(): دریافت مقدار ذخیره‌شده در کانتکست جاری
  • enterWith(store): ورود به کانتکست مشخص برای اجرای همگام
  • exit(callback): خروج از کانتکست و اجرای تابع خارج از آن
  • disable(): غیرفعال‌سازی نمونه برای آزادسازی حافظه
  • bind(fn) و snapshot(): اتصال یا گرفتن snapshot از کانتکست جاری

3. AsyncResource


کلاس AsyncResource برای توسعه‌دهندگان طراحی شده تا بتوانند منابع ناهمگام سفارشی بسازند و چرخهٔ عمر آن‌ها را مدیریت کنند.

ویژگی‌ها:

  • runInAsyncScope(fn, thisArg, ...args): اجرای تابع در کانتکست منبع ناهمگام
  • emitDestroy(): فراخوانی hookهای تخریب
  • asyncId(): شناسهٔ یکتا برای منبع
  • triggerAsyncId(): شناسهٔ منبعی که این منبع را ایجاد کرده

مثال:

class DBQuery extends AsyncResource {
  constructor(db) {
    super('DBQuery');
    this.db = db;
  }

  getInfo(query, callback) {
    this.db.get(query, (err, data) => {
      this.runInAsyncScope(callback, null, err, data);
    });
  }

  close() {
    this.db = null;
    this.emitDestroy();
  }
}

4. استفاده در Worker Pool


با استفاده از AsyncResource می‌توان یک Worker Pool ساخت که وظایف را به‌درستی ردیابی کند و کانتکست‌ها را حفظ نماید.

class WorkerPoolTaskInfo extends AsyncResource {
  constructor(callback) {
    super('WorkerPoolTaskInfo');
    this.callback = callback;
  }

  done(err, result) {
    this.runInAsyncScope(this.callback, null, err, result);
    this.emitDestroy();
  }
}

این الگو برای مدیریت منابعی مانند پایگاه داده یا thread pool بسیار کاربردی است.

5. ادغام با EventEmitter


EventEmitterها ممکن است در کانتکست متفاوتی اجرا شوند. با استفاده از AsyncResource.bind() می‌توان اطمینان حاصل کرد که listenerها در کانتکست صحیح اجرا می‌شوند.

req.on('close', AsyncResource.bind(() => {
  // Execution context is bound correctly
}));

نتیجه‌گیری


ماژول async_hooks در Node.js ابزار قدرتمندی برای مدیریت کانتکست‌های ناهمگام است. با استفاده از AsyncLocalStorage و AsyncResource می‌توان داده‌ها را در طول چرخهٔ عمر عملیات ناهمگام حفظ کرد، منابع سفارشی ساخت و از بروز مشکلاتی مانند context loss جلوگیری نمود. این قابلیت‌ها برای ساخت برنامه‌های وب مقیاس‌پذیر و پایدار حیاتی هستند.

نوشته و پژوهش شده توسط دکتر شاهین صیامی