日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
深入理解Node.js的Asynchooks

雖然Async hooks至此還是實(shí)驗(yàn)性API,但是他的確可以解決應(yīng)用中的一些問題,比如日志和調(diào)用棧跟蹤。本文從應(yīng)用和原理方便介紹一下Node.js的Async hooks。

大悟網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),大悟網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為大悟千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個(gè)售后服務(wù)好的大悟做網(wǎng)站的公司定做!

1 env中的AsyncHooks

在Node.js的env對象中有一個(gè)AsyncHooks對象,負(fù)責(zé)Node.js進(jìn)程中async_hooks的管理。我們看一下定義。

1.1 類定義

 
 
 
 
  1. class AsyncHooks : public MemoryRetainer { 
  2.  public: 
  3.  
  4.   enum Fields { 
  5.     // 五種鉤子 
  6.     kInit, 
  7.     kBefore, 
  8.     kAfter, 
  9.     kDestroy, 
  10.     kPromiseResolve, 
  11.     // 鉤子總數(shù) 
  12.     kTotals, 
  13.     // async_hooks開啟的個(gè)數(shù) 
  14.     kCheck, 
  15.     // 記錄棧的top指針 
  16.     kStackLength, 
  17.     // 數(shù)組大小 
  18.     kFieldsCount, 
  19.   }; 
  20.  
  21.   enum UidFields { 
  22.     kExecutionAsyncId, 
  23.     kTriggerAsyncId, 
  24.     // 當(dāng)前async id的值 
  25.     kAsyncIdCounter, 
  26.     kDefaultTriggerAsyncId, 
  27.     kUidFieldsCount, 
  28.   }; 
  29.  
  30.  private: 
  31.   inline AsyncHooks(); 
  32.   // 異步資源的類型 
  33.   std::array, AsyncWrap::PROVIDERS_LENGTH> providers_; 
  34.   // 棧 
  35.   AliasedFloat64Array async_ids_stack_; 
  36.   // 整形數(shù)組,每個(gè)元素值的意義和Fields對應(yīng) 
  37.   AliasedUint32Array fields_; 
  38.   // 整形數(shù)組,每個(gè)元素值的意義和UidFields對應(yīng) 
  39.   AliasedFloat64Array async_id_fields_; 
  40. }; 

結(jié)構(gòu)圖如下

接下來看一下env的AsyncHooks對象提供了哪些API,這些API是上層的基礎(chǔ)。

1.2 讀API

我們看一下env對象中獲取AsyncHooks對象對應(yīng)字段的API。

 
 
 
 
  1. // 獲取對應(yīng)的字段 
  2. inline AliasedUint32Array& AsyncHooks::fields() { 
  3.   return fields_; 
  4.  
  5. inline AliasedFloat64Array& AsyncHooks::async_id_fields() { 
  6.   return async_id_fields_; 
  7.  
  8. inline AliasedFloat64Array& AsyncHooks::async_ids_stack() { 
  9.   return async_ids_stack_; 
  10.  
  11. // 獲取資源類型 
  12. inline v8::Local AsyncHooks::provider_string(int idx) { 
  13.   return providers_[idx].Get(env()->isolate()); 
  14.  
  15. // 新建資源的時(shí)候,獲取新的async id 
  16. inline double Environment::new_async_id() { 
  17.   async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1; 
  18.   return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter]; 
  19.  
  20. // 獲取當(dāng)前async id 
  21. inline double Environment::execution_async_id() { 
  22.   return async_hooks()->async_id_fields()[AsyncHooks::kExecutionAsyncId]; 
  23.  
  24. // 獲取當(dāng)前trigger async id 
  25. inline double Environment::trigger_async_id() { 
  26.   return async_hooks()->async_id_fields()[AsyncHooks::kTriggerAsyncId]; 
  27.  
  28. // 獲取默認(rèn)的trigger async id,如果沒有設(shè)置,則獲取當(dāng)前的async id 
  29. inline double Environment::get_default_trigger_async_id() { 
  30.   double default_trigger_async_id = async_hooks()->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId]; 
  31.   // If defaultTriggerAsyncId isn't set, use the executionAsyncId 
  32.   if (default_trigger_async_id < 0) 
  33.     default_trigger_async_id = execution_async_id(); 
  34.   return default_trigger_async_id; 

1.3 寫API

 
 
 
 
  1. inline void AsyncHooks::push_async_ids(double async_id, 
  2.                                        double trigger_async_id) { 
  3.   // 獲取當(dāng)前棧頂指針 
  4.   uint32_t offset = fields_[kStackLength]; 
  5.   // 不夠則擴(kuò)容 
  6.   if (offset * 2 >= async_ids_stack_.Length()) 
  7.     grow_async_ids_stack(); 
  8.   // 把舊的上下文壓棧   
  9.   async_ids_stack_[2 * offset] = async_id_fields_[kExecutionAsyncId]; 
  10.   async_ids_stack_[2 * offset + 1] = async_id_fields_[kTriggerAsyncId]; 
  11.   // 棧指針加一 
  12.   fields_[kStackLength] += 1; 
  13.   // 記錄當(dāng)前上下文 
  14.   async_id_fields_[kExecutionAsyncId] = async_id; 
  15.   async_id_fields_[kTriggerAsyncId] = trigger_async_id; 
  16.  
  17. // 和上面的邏輯相反 
  18. inline bool AsyncHooks::pop_async_id(double async_id) { 
  19.  
  20.   if (fields_[kStackLength] == 0) return false; 
  21.   uint32_t offset = fields_[kStackLength] - 1; 
  22.   async_id_fields_[kExecutionAsyncId] = async_ids_stack_[2 * offset]; 
  23.   async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1]; 
  24.   fields_[kStackLength] = offset; 
  25.  
  26.   return fields_[kStackLength] > 0; 

2 底層資源封裝類 - AsyncWrap

接著看一下異步資源的基類AsyncWrap。所有依賴于C、C++層實(shí)現(xiàn)的資源(比如TCP、UDP)都會繼承AsyncWrap。看看該類的定義。

 
 
 
 
  1. class AsyncWrap : public BaseObject { 
  2.  private: 
  3.   ProviderType provider_type_ = PROVIDER_NONE; 
  4.   double async_id_ = kInvalidAsyncId; 
  5.   double trigger_async_id_; 
  6. }; 

我們看到每個(gè)AsyncWrap對象都有async_id_、trigger_async_id_和provider_type_屬性,這正是在init回調(diào)里拿到的數(shù)據(jù)。我們看看AsyncWrap的構(gòu)造函數(shù)。接下來看一下新建一個(gè)資源(AsyncWrap)時(shí)的邏輯。

2.1 資源初始化

 
 
 
 
  1. AsyncWrap::AsyncWrap(Environment* env, 
  2.                      Local object, 
  3.                      ProviderType provider, 
  4.                      double execution_async_id, 
  5.                      bool silent) 
  6.     : AsyncWrap(env, object) { 
  7.   // 資源類型 
  8.   provider_type_ = provider; 
  9.   AsyncReset(execution_async_id, silent); 
  10.  
  11. void AsyncWrap::AsyncReset(Local resource, double execution_async_id, 
  12.                            bool silent) { 
  13.   // 獲取一個(gè)新的async id,execution_async_id默認(rèn)是kInvalidAsyncId 
  14.   async_id_ = execution_async_id == kInvalidAsyncId ? env()->new_async_id() 
  15.                                                      : execution_async_id; 
  16.   // 獲取trigger async id                                                    
  17.   trigger_async_id_ = env()->get_default_trigger_async_id(); 
  18.   // 執(zhí)行init鉤子 
  19.   EmitAsyncInit(env(), resource, 
  20.                 env()->async_hooks()->provider_string(provider_type()), 
  21.                 async_id_, trigger_async_id_); 
  22. 接著看EmitAsyncInit

     
     
     
     
    1. void AsyncWrap::EmitAsyncInit(Environment* env, 
    2.                               Local object, 
    3.                               Local type, 
    4.                               double async_id, 
    5.                               double trigger_async_id) { 
    6.   AsyncHooks* async_hooks = env->async_hooks(); 
    7.   HandleScope scope(env->isolate()); 
    8.   Local init_fn = env->async_hooks_init_function(); 
    9.  
    10.   Local argv[] = { 
    11.     Number::New(env->isolate(), async_id), 
    12.     type, 
    13.     Number::New(env->isolate(), trigger_async_id), 
    14.     object, 
    15.   }; 
    16.  
    17.   TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal); 
    18.   // 執(zhí)行init回調(diào) 
    19.   USE(init_fn->Call(env->context(), object, arraysize(argv), argv)); 
    20. 那么env->async_hooks_init_function()的值是什么呢?這是在Node.js初始化時(shí)設(shè)置的。

       
       
       
       
      1. const { nativeHooks } = require('internal/async_hooks'); 
      2. internalBinding('async_wrap').setupHooks(nativeHooks); 

      SetupHooks的實(shí)現(xiàn)如下

       
       
       
       
      1. static void SetupHooks(const FunctionCallbackInfo& args) { 
      2.  
      3.   Environment* env = Environment::GetCurrent(args); 
      4.   Local fn_obj = args[0].As();#define SET_HOOK_FN(name)                                                      \ 
      5.   do {                                                                         \ 
      6.     Local v =                                                           \ 
      7.         fn_obj->Get(env->context(),                                            \ 
      8.                     FIXED_ONE_BYTE_STRING(env->isolate(), #name))              \ 
      9.             .ToLocalChecked();                                                 \ 
      10.     CHECK(v->IsFunction());                                                    \ 
      11.     env->set_async_hooks_##name##_function(v.As());                  \ 
      12.   } while (0) 
      13.   // 保存到env中 
      14.   SET_HOOK_FN(init); 
      15.   SET_HOOK_FN(before); 
      16.   SET_HOOK_FN(after); 
      17.   SET_HOOK_FN(destroy); 
      18.   SET_HOOK_FN(promise_resolve);#undef SET_HOOK_FN 
      19. nativeHooks的實(shí)現(xiàn)如下

         
         
         
         
        1. nativeHooks: { 
        2.   init: emitInitNative, 
        3.   before: emitBeforeNative, 
        4.   after: emitAfterNative, 
        5.   destroy: emitDestroyNative, 
        6.   promise_resolve: emitPromiseResolveNative 

        這些Hooks會執(zhí)行對應(yīng)的回調(diào),比如emitInitNative

         
         
         
         
        1. function emitInitNative(asyncId, type, triggerAsyncId, resource) { 
        2.   for (var i = 0; i < active_hooks.array.length; i++) { 
        3.       if (typeof active_hooks.array[i][init_symbol] === 'function') { 
        4.         active_hooks.array[i][init_symbol]( 
        5.           asyncId, type, triggerAsyncId, 
        6.           resource 
        7.         ); 
        8.       } 
        9.   } 

        active_hooks.array的值就是我們在業(yè)務(wù)代碼里設(shè)置的鉤子,每次調(diào)研createHooks的時(shí)候就對應(yīng)數(shù)組的一個(gè)元素。

        2.2 執(zhí)行資源回調(diào)

        當(dāng)業(yè)務(wù)代碼異步請求底層API,并且底層滿足條件時(shí),就會執(zhí)行上層的回調(diào),比如監(jiān)聽一個(gè)socket時(shí),有連接到來。Node.js就會調(diào)用MakeCallback函數(shù)執(zhí)行回調(diào)。

         
         
         
         
        1. MaybeLocal AsyncWrap::MakeCallback(const Local cb, 
        2.                                           int argc, 
        3.                                           Local* argv) { 
        4.   // 當(dāng)前AsyncWrap對象對應(yīng)的執(zhí)行上下文                              
        5.   ProviderType provider = provider_type(); 
        6.   async_context context { get_async_id(), get_trigger_async_id() }; 
        7.   MaybeLocal ret = InternalMakeCallback(env(), object(), cb, argc, argv, context); 
        8.  
        9.   return ret; 

        MaybeLocal InternalMakeCallback(Environment* env,

         
         
         
         
        1. MaybeLocal InternalMakeCallback(Environment* env, 
        2.                                        Local recv, 
        3.                                        const Local callback, 
        4.                                        int argc, 
        5.                                        Local argv[], 
        6.                                        async_context asyncContext) { 
        7.   // 新建一個(gè)scope                                      
        8.   InternalCallbackScope scope(env, recv, asyncContext); 
        9.   // 執(zhí)行回調(diào) 
        10.   callback->Call(env->context(), recv, argc, argv); 
        11.   // 關(guān)閉scope 
        12.   scope.Close(); 
        13. 我們看看新建和關(guān)閉scope都做了什么事情。

           
           
           
           
          1. InternalCallbackScope::InternalCallbackScope(Environment* env, 
          2.                                              Local object, 
          3.                                              const async_context& asyncContext, 
          4.                                              int flags) 
          5.   : env_(env), 
          6.     async_context_(asyncContext), 
          7.     object_(object), 
          8.     skip_hooks_(flags & kSkipAsyncHooks), 
          9.     skip_task_queues_(flags & kSkipTaskQueues) { 
          10.   // v14版本中,是先觸發(fā)before再push上下文,順序是不對的,v16已經(jīng)改過來。 
          11.   // 當(dāng)前執(zhí)行上下文入棧 
          12.   env->async_hooks()->push_async_ids(async_context_.async_id, 
          13.                                async_context_.trigger_async_id); 
          14.   // 觸發(fā)before鉤子 
          15.   if (asyncContext.async_id != 0 && !skip_hooks_) { 
          16.     AsyncWrap::EmitBefore(env, asyncContext.async_id); 
          17.   } 
          18.  
          19.   pushed_ids_ = true; 
          20. 在scope里會把當(dāng)前AsyncWrap對象的執(zhí)行上下文作為當(dāng)前執(zhí)行上下文,并且觸發(fā)before鉤子,然后執(zhí)行業(yè)務(wù)回調(diào),所以我們在回調(diào)里獲取當(dāng)前執(zhí)行上下文時(shí)就拿到了AsyncWrap對應(yīng)的值( 調(diào)用executionAsyncId),接著看Close

             
             
             
             
            1. void InternalCallbackScope::Close() { 
            2.   // 執(zhí)行 
            3.   if (pushed_ids_) 
            4.     env_->async_hooks()->pop_async_id(async_context_.async_id); 
            5.  
            6.   if (async_context_.async_id != 0 && !skip_hooks_) { 
            7.     AsyncWrap::EmitAfter(env_, async_context_.async_id); 
            8.   } 

            Close在執(zhí)行回調(diào)后被調(diào)用,主要是恢復(fù)當(dāng)前執(zhí)行上下文并且觸發(fā)after鉤子。

            3 上層資源的封裝 - Timeout、TickObjecd等

            并不是所有的異步資源都是底層實(shí)現(xiàn)的,比如定時(shí)器,tick也被定義為異步資源,因?yàn)樗麄兌际呛突卣{(diào)相關(guān)。這種異步資源是在JS層實(shí)現(xiàn)的,這里只分析Timeout。

            3.1 創(chuàng)建資源

            我們看一下執(zhí)行setTimeout時(shí)的核心邏輯。

             
             
             
             
            1. function setTimeout(callback, after, arg1, arg2, arg3) { 
            2.   const timeout = new Timeout(callback, after, args, false, true); 
            3.   return timeout; 
            4.  
            5. function Timeout(callback, after, args, isRepeat, isRefed) { 
            6.   initAsyncResource(this, 'Timeout'); 
            7.  
            8. function initAsyncResource(resource, type) { 
            9.   // 獲取新的async id 
            10.   const asyncId = resource[async_id_symbol] = newAsyncId(); 
            11.   const triggerAsyncId = resource[trigger_async_id_symbol] = getDefaultTriggerAsyncId(); 
            12.   // 是否設(shè)置了init鉤子,是則觸發(fā)回調(diào) 
            13.   if (initHooksExist()) 
            14.     emitInit(asyncId, type, triggerAsyncId, resource); 

            執(zhí)行setTimeout時(shí),Node.js會創(chuàng)建一個(gè)Timeout對象,設(shè)置async_hooks相關(guān)的上下文并記錄到Timeout對象中。然后觸發(fā)init鉤子。

             
             
             
             
            1. function emitInitScript(asyncId, type, triggerAsyncId, resource) { 
            2.   emitInitNative(asyncId, type, triggerAsyncId, resource); 

            以上代碼會執(zhí)行每個(gè)async_hooks對象的init回調(diào)(通常我們只有一個(gè)async_hooks對象)。

            3.2執(zhí)行回調(diào)

            當(dāng)定時(shí)器到期時(shí),會執(zhí)行回調(diào),我們看看相關(guān)的邏輯。

             
             
             
             
            1. // 觸發(fā)before鉤子 
            2. emitBefore(asyncId, timer[trigger_async_id_symbol]); 
            3. // 執(zhí)行回調(diào) 
            4. timer._onTimeout(); 
            5. // 觸發(fā)after回調(diào)emitAfter(asyncId); 

            我們看到執(zhí)行超時(shí)回調(diào)的前后會觸發(fā)對應(yīng)的鉤子。

             
             
             
             
            1. function emitBeforeScript(asyncId, triggerAsyncId) { 
            2.   // 和底層的push_async_ids邏輯一樣 
            3.   pushAsyncIds(asyncId, triggerAsyncId); 
            4.   // 如果有回調(diào)則執(zhí)行 
            5.   if (async_hook_fields[kBefore] > 0) 
            6.     emitBeforeNative(asyncId); 
            7.  
            8. function emitAfterScript(asyncId) { 
            9.   // 設(shè)置了after回調(diào)則emit 
            10.   if (async_hook_fields[kAfter] > 0) 
            11.     emitAfterNative(asyncId); 
            12.   // 和底層的pop_async_ids邏輯一樣 
            13.   popAsyncIds(asyncId); 

            JS層的實(shí)現(xiàn)和底層是保持一致的。如果我們在setTimeout回調(diào)里新建一個(gè)資源,比如再次執(zhí)行setTimeout,這時(shí)候trigger async id就是第一個(gè)setTimeout對應(yīng)的async id,所以就連起來了,后面我們會看到具體的例子。

            4 DefaultTriggerAsyncIdScope

            Node.js為了避免過多通過參數(shù)傳遞的方式傳遞async id,就設(shè)計(jì)了DefaultTriggerAsyncIdScope。DefaultTriggerAsyncIdScope的作用類似在多個(gè)函數(shù)外維護(hù)一個(gè)變量,多個(gè)函數(shù)都可以通過DefaultTriggerAsyncIdScope獲得trigger async id,而不需要通過層層傳遞的方式,他的實(shí)現(xiàn)非常簡單。

             
             
             
             
            1. class DefaultTriggerAsyncIdScope { 
            2.    private: 
            3.     AsyncHooks* async_hooks_; 
            4.     double old_default_trigger_async_id_; 
            5. }; 
            6.  
            7. inline AsyncHooks::DefaultTriggerAsyncIdScope ::DefaultTriggerAsyncIdScope( 
            8.     Environment* env, double default_trigger_async_id) 
            9.     : async_hooks_(env->async_hooks()) { 
            10.  
            11.   // 記錄舊的id,設(shè)置新的id 
            12.   old_default_trigger_async_id_ = 
            13.     async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId]; 
            14.   async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = 
            15.     default_trigger_async_id; 
            16.  
            17. // 恢復(fù) 
            18. inline AsyncHooks::DefaultTriggerAsyncIdScope ::~DefaultTriggerAsyncIdScope() { 
            19.   async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] = 
            20.     old_default_trigger_async_id_; 

            DefaultTriggerAsyncIdScope主要是記錄舊的id,然后把新的id設(shè)置到env中,當(dāng)其他函數(shù)調(diào)用get_default_trigger_async_id時(shí)就可以獲取設(shè)置的async id。同樣JS層也實(shí)現(xiàn)了類似的API。

             
             
             
             
            1. function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { 
            2.   const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; 
            3.   async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; 
            4.  
            5.   try { 
            6.     return block(...args); 
            7.   } finally { 
            8.     async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; 
            9.   } 

            在執(zhí)行block函數(shù)時(shí),可以獲取到設(shè)置的值,而不需要傳遞,執(zhí)行完block后恢復(fù)。我們看看如何使用。下面摘自net模塊的代碼。

             
             
             
             
            1. // 獲取handle里的async id 
            2. this[async_id_symbol] = getNewAsyncId(this._handle); 
            3. defaultTriggerAsyncIdScope(this[async_id_symbol], 
            4.                              process.nextTick, 
            5.                              emitListeningNT, 
            6.                              this); 

            我們看一下這里具體的情況。在defaultTriggerAsyncIdScope中會以emitListeningNT為入?yún)?zhí)行process.nextTick。我們看看nextTick的實(shí)現(xiàn)。

             
             
             
             
            1. function nextTick(callback) { 
            2.   // 獲取新的async id 
            3.   const asyncId = newAsyncId(); 
            4.   // 獲取默認(rèn)的trigger async id,即剛才設(shè)置的 
            5.   const triggerAsyncId = getDefaultTriggerAsyncId(); 
            6.   const tickObject = { 
            7.     [async_id_symbol]: asyncId, 
            8.     [trigger_async_id_symbol]: triggerAsyncId, 
            9.     callback, 
            10.     args 
            11.   }; 
            12.   if (initHooksExist()) 
            13.     // 創(chuàng)建了新的資源,觸發(fā)init鉤子 
            14.     emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject); 
            15.   queue.push(tickObject); 

            我們看到在nextTick中通過getDefaultTriggerAsyncId拿到了trigger async id。

             
             
             
             
            1. function getDefaultTriggerAsyncId() { 
            2.   const defaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; 
            3.   if (defaultTriggerAsyncId < 0) 
            4.     return async_id_fields[kExecutionAsyncId]; 
            5.   return defaultTriggerAsyncId; 

            getDefaultTriggerAsyncId返回的就是剛才通過defaultTriggerAsyncIdScope設(shè)置的async id。所以在觸發(fā)TickObject的init鉤子時(shí)用戶就可以拿到對應(yīng)的id。不過更重要的時(shí),在異步執(zhí)行nextTick的任務(wù)時(shí),還可以拿到原始的trigger async id。因?yàn)樵搃d記錄在tickObject中。我們看看執(zhí)行tick任務(wù)時(shí)的邏輯。

             
             
             
             
            1. function processTicksAndRejections() { 
            2.   let tock; 
            3.   do { 
            4.     while (tock = queue.shift()) { 
            5.       // 拿到對應(yīng)的async 上下文 
            6.       const asyncId = tock[async_id_symbol]; 
            7.       emitBefore(asyncId, tock[trigger_async_id_symbol]); 
            8.       try { 
            9.         const callback = tock.callback; 
            10.         callback(); 
            11.       } finally { 
            12.         if (destroyHooksExist()) 
            13.           emitDestroy(asyncId); 
            14.       } 
            15.       emitAfter(asyncId); 
            16.     } 
            17.   } while (!queue.isEmpty() || processPromiseRejections()); 

            5 資源銷毀

            資源銷毀的時(shí)候也會觸發(fā)對應(yīng)的鉤子,不過不同的是這個(gè)鉤子是異步觸發(fā)的。無論是JS還是好C++層觸發(fā)銷毀鉤子的時(shí)候,邏輯都是一致的。

             
             
             
             
            1. void AsyncWrap::EmitDestroy(Environment* env, double async_id) { 
            2.   // 之前為空則設(shè)置回調(diào) 
            3.   if (env->destroy_async_id_list()->empty()) { 
            4.     env->SetUnrefImmediate(&DestroyAsyncIdsCallback); 
            5.   } 
            6.   // async id入隊(duì) 
            7.   env->destroy_async_id_list()->push_back(async_id); 
            8.  
            9. template void Environment::SetUnrefImmediate(Fn&& cb) { 
            10.   CreateImmediate(std::move(cb), false); 
            11.  
            12. template void Environment::CreateImmediate(Fn&& cb, bool ref) { 
            13.   auto callback = std::make_unique>( 
            14.       std::move(cb), ref); 
            15.   // 加入任務(wù)隊(duì)列     
            16.   native_immediates_.Push(std::move(callback)); 

            在事件循環(huán)的check階段就會執(zhí)行里面的任務(wù),從而執(zhí)行回調(diào)DestroyAsyncIdsCallback。

             
             
             
             
            1. void AsyncWrap::DestroyAsyncIdsCallback(Environment* env) { 
            2.   Local fn = env->async_hooks_destroy_function(); 
            3.   do { 
            4.     std::vector destroy_async_id_list; 
            5.     destroy_async_id_list.swap(*env->destroy_async_id_list()); 
            6.     // 遍歷銷毀的async id 
            7.     for (auto async_id : destroy_async_id_list) { 
            8.       HandleScope scope(env->isolate()); 
            9.       Local async_id_value = Number::New(env->isolate(), async_id); 
            10.       // 執(zhí)行JS層回調(diào) 
            11.       MaybeLocal ret = fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value); 
            12.     } 
            13.   } while (!env->destroy_async_id_list()->empty()); 

            6 Async hooks的使用

            我們通常以以下方式使用Async hooks

             
             
             
             
            1. const async_hooks = require('async_hooks'); 
            2. async_hooks.createHook({ 
            3.   init(asyncId, type, triggerAsyncId) {}, 
            4.   before(asyncId) {}, 
            5.   after(asyncId) {}, 
            6.   destroy(asyncId) {}, 
            7.   promiseResolve(asyncId), 
            8. }).enable(); 

            async_hooks是對資源生命周期的抽象,資源就是操作對象和回調(diào)的抽象。async_hooks定義了五個(gè)生命周期鉤子,當(dāng)資源的狀態(tài)到達(dá)某個(gè)周期節(jié)點(diǎn)時(shí),async_hooks就會觸發(fā)對應(yīng)的鉤子。下面我們看一下具體的實(shí)現(xiàn)。我們首先看一下createHook。

             
             
             
             
            1. function createHook(fns) { 
            2.   return new AsyncHook(fns); 

            createHook是對AsyncHook的封裝

             
             
             
             
            1. class AsyncHook { 
            2.   constructor({ init, before, after, destroy, promiseResolve }) { 
            3.     // 記錄回調(diào) 
            4.     this[init_symbol] = init; 
            5.     this[before_symbol] = before; 
            6.     this[after_symbol] = after; 
            7.     this[destroy_symbol] = destroy; 
            8.     this[promise_resolve_symbol] = promiseResolve; 
            9.   } 

            AsyncHook的初始化很簡單,創(chuàng)建一個(gè)AsyncHook對象記錄回調(diào)函數(shù)。創(chuàng)建了AsyncHook之后,我們需要調(diào)用AsyncHook的enable函數(shù)手動開啟。

             
             
             
             
            1. class AsyncHook { 
            2.   enable() { 
            3.     // 獲取一個(gè)AsyncHook對象數(shù)組和一個(gè)整形數(shù)組 
            4.     const [hooks_array, hook_fields] = getHookArrays(); 
            5.     // 執(zhí)行過enable了則不需要再執(zhí)行 
            6.     if (hooks_array.includes(this)) 
            7.       return this; 
            8.     // 做些統(tǒng)計(jì) 
            9.     const prev_kTotals = hook_fields[kTotals]; 
            10.     hook_fields[kTotals] = hook_fields[kInit] += +!!this[init_symbol]; 
            11.     hook_fields[kTotals] += hook_fields[kBefore] += +!!this[before_symbol]; 
            12.     hook_fields[kTotals] += hook_fields[kAfter] += +!!this[after_symbol]; 
            13.     hook_fields[kTotals] += hook_fields[kDestroy] += +!!this[destroy_symbol]; 
            14.     hook_fields[kTotals] += 
            15.         hook_fields[kPromiseResolve] += +!!this[promise_resolve_symbol]; 
            16.     // 當(dāng)前對象插入數(shù)組中 
            17.     hooks_array.push(this); 
            18.     // 如果之前的數(shù)量是0,本次操作后大于0則開啟底層的邏輯 
            19.     if (prev_kTotals === 0 && hook_fields[kTotals] > 0) { 
            20.       enableHooks(); 
            21.     } 
            22.  
            23.     return this; 
            24.   } 

            1 hooks_array:是一個(gè)AsyncHook對象數(shù)組,主要用于記錄用戶創(chuàng)建了哪些AsyncHook對象,然后哪些AsyncHook對象里都設(shè)置了哪些鉤子,在回調(diào)的時(shí)候就會遍歷這個(gè)對象數(shù)組,執(zhí)行里面的回調(diào)。

            2 hook_fields:對應(yīng)底層的async_hook_fields。

            3 enableHooks:

             
             
             
             
            1. function enableHooks() { 
            2.   // 記錄async_hooks的開啟個(gè)數(shù) 
            3.   async_hook_fields[kCheck] += 1; 

            至此,async_hooks的初始化就完成了,我們發(fā)現(xiàn)邏輯非常簡單。下面我們看一下他是如何串起來的。下面我們以TCP模塊為例。

             
             
             
             
            1. const { createHook, executionAsyncId } = require('async_hooks'); 
            2. const fs = require('fs'); 
            3. const net = require('net'); 
            4. createHook({ 
            5.   init(asyncId, type, triggerAsyncId) { 
            6.     fs.writeSync( 
            7.       1, 
            8.       `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${executionAsyncId()}\n`); 
            9.   }}).enable(); 
            10.  
            11. net.createServer((conn) => {}).listen(8080); 

            以上代碼輸出

             
             
             
             
            1. init: type: TCPSERVERWRAP asyncId: 2 trigger id: 1 executionAsyncId(): 1 triggerAsyncId(): 0 
            2. init: type: TickObject asyncId: 3 trigger id: 2 executionAsyncId(): 1 triggerAsyncId(): 0 
            3. before: asyncId: 3 executionAsyncId(): 3 triggerAsyncId(): 2 
            4. after: asyncId: 3 executionAsyncId(): 3 triggerAsyncId(): 2 

            下面我們來分析具體過程。我們知道創(chuàng)建資源的時(shí)候會執(zhí)行init回調(diào),具體邏輯在listen函數(shù)中,在listen函數(shù)中,通過層層調(diào)用會執(zhí)行new TCP新建一個(gè)對象,表示服務(wù)器。TCP是C++層導(dǎo)出的類,剛才我們說過,TCP會繼承AsyncWrap,新建AsyncWrap對象的時(shí)候會觸發(fā)init鉤子,結(jié)構(gòu)圖如下。

            對應(yīng)輸出

             
             
             
             
            1. init: type: TCPSERVERWRAP asyncId: 2 trigger id: 1 executionAsyncId(): 1 triggerAsyncId(): 0 

            那TickObject是怎么來的呢?我們接著看listen里的另一段邏輯。

             
             
             
             
            1. this[async_id_symbol] = getNewAsyncId(this._handle); 
            2. defaultTriggerAsyncIdScope(this[async_id_symbol], 
            3.                            process.nextTick, 
            4.                            emitListeningNT, 
            5.                            this); 

            上面的代碼我們剛才已經(jīng)分析過,在執(zhí)行process.nextTick的時(shí)候會創(chuàng)建一個(gè)TickObject對象封裝執(zhí)行上下文和回調(diào)。

             
             
             
             
            1. const asyncId = newAsyncId(); 
            2. const triggerAsyncId = getDefaultTriggerAsyncId(); 
            3. const tickObject = { 
            4.   [async_id_symbol]: asyncId, 
            5.   [trigger_async_id_symbol]: triggerAsyncId, 
            6.   callback, 
            7.   args 
            8. }; 
            9. emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject); 

            emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject);

            這次再次觸發(fā)了init鉤子,結(jié)構(gòu)如下(nextTick通過getDefaultTriggerAsyncId獲取的id是defaultTriggerAsyncIdScope設(shè)置的id)。

            對應(yīng)輸出

             
             
             
             
            1. init: type: TickObject asyncId: 3 trigger id: 2 executionAsyncId(): 1 triggerAsyncId(): 0 

            接著執(zhí)行tick任務(wù)。

             
             
             
             
            1. const asyncId = tock[async_id_symbol]; 
            2. emitBefore(asyncId, tock[trigger_async_id_symbol]); 
            3. try { 
            4.   tock.callback(); 
            5. } finally { 
            6.   if (destroyHooksExist()) 
            7.     emitDestroy(asyncId); 
            8. emitAfter(asyncId); 

            emitBefore時(shí),結(jié)構(gòu)圖如下。

            對應(yīng)輸出

             
             
             
             
            1. before: asyncId: 3 executionAsyncId(): 3 triggerAsyncId(): 2 
            2. after: asyncId: 3 executionAsyncId(): 3 triggerAsyncId(): 2 

            執(zhí)行完我們的JS代碼后,所有入棧的上下文都會被清空,結(jié)構(gòu)圖如下。

            如果這時(shí)候有一個(gè)連接
            文章標(biāo)題:深入理解Node.js的Asynchooks
            新聞來源:http://www.5511xx.com/article/cdgdsjh.html