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

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
穿針引線之AsyncLocalStorage

在 Node.js 中,如何更優(yōu)雅地獲取請(qǐng)求上下文一直是一個(gè)問題,看一下下面的例子。

背景

const http = require('http');
function handler1(req, res) {
    console.log(req.url);
}

function handler2(req, res) {
     console.log(req.url);
}

http.createServer((req, res) => {
    handler1(req, res);
    handler2(req, res);
    res.end();
}).listen();

上面的例子中,每次收到一個(gè)請(qǐng)求時(shí)都會(huì)執(zhí)行 handler1 和 handler2,為了在不同的地方里都能拿到請(qǐng)求上下文,我們只能逐級(jí)進(jìn)行傳遞,如果業(yè)務(wù)邏輯很復(fù)雜,這個(gè)維護(hù)性是非常差的,下接下來看看如何使用 AsyncLocalStorage 解決這個(gè)問題。

AsyncLocalStorage

AsyncLocalStorage 是基于 Async Hooks 實(shí)現(xiàn)的,它通過上下文傳遞實(shí)現(xiàn)了異步代碼的上下文共享和隔離。下面看一個(gè)例子。

const { AsyncLocalStorage } = require('async_hooks');

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

asyncLocalStorage.run(1, () => {
    logWithId('start');
    setImmediate(() => {
      logWithId('finish');
    });
 });

上面的代碼會(huì)輸出

1: start
1: finish

從中可以看到兩個(gè) logWithId 共享了同一個(gè)上下文,這個(gè)上下文是由 run 函數(shù)設(shè)置的 1,那這種技術(shù)如何解決我們剛開始提出的問題呢?看一下下面的例子。

const http = require('http');
const { AsyncLocalStorage } = require('async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

function handler1() {
    const { req } = asyncLocalStorage.getStore();
    console.log(req.url);
}

function handler2() {
    setImmediate(() => {
        const { req } = asyncLocalStorage.getStore();
        console.log(req.url);
    });
}

http.createServer((req, res) => {
    asyncLocalStorage.run({ req, res }, () => {   
        handler1();
        handler2();
    });
    res.end();
}).listen(9999, () => {
    http.get({ port: 9999, path: '/test' })
});

執(zhí)行上面代碼輸出如下。

/test
/test

可以看到,我們不需要逐級(jí)地傳遞請(qǐng)求上下文并且可以在任意異步代碼中獲取請(qǐng)求上下文。這讓代碼的編寫和維護(hù)帶來了非常大的好處,不過缺點(diǎn)就是,因?yàn)?AsyncLocalStorage 是基于 Async hooks 的,所以會(huì)帶來一些性能損耗,不同的版本可能不一樣,但是 Node.js 也在不斷地優(yōu)化其性能,我印象中,社區(qū)有人提過使用其他技術(shù)實(shí)現(xiàn) AsyncLocalStorage。

AsyncLocalStorage 原理

知其然知其所以然,只知道怎么使用是不夠的,理解其原理可以幫助我們更好地使用它。下面來分析一下 AsyncLocalStorage 的原理。先看一下創(chuàng)建 AsyncLocalStorage 的邏輯

class AsyncLocalStorage {
  constructor() {
    this.kResourceStore = Symbol('kResourceStore');
    this.enabled = false;
  }
}

創(chuàng)建AsyncLocalStorage的時(shí)候很簡(jiǎn)單,主要是置狀態(tài)為false,并且設(shè)置kResourceStore的值為Symbol('kResourceStore')。設(shè)置為Symbol('kResourceStore')而不是 'kResourceStore' 很重要,我們后面會(huì)看到。繼續(xù)看一下執(zhí)行AsyncLocalStorage.run的邏輯。

const storageList = [];
const storageHook = createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    const currentResource = executionAsyncResource();
    // 傳遞上下文
    for (let i = 0; i < storageList.length; ++i) {
      storageList[i]._propagate(resource, currentResource, type);
    }
  },
});

run(store, callback, ...args) {
    // 把當(dāng)前 AsyncLocalStorage 加入隊(duì)列
    ArrayPrototypePush(storageList, this);
    // 啟動(dòng) AsyncHooks
    storageHook.enable();
   // 獲取當(dāng)前的異步資源,比如收到的請(qǐng)求
   const resource = executionAsyncResource();
   // 記錄舊的上下文
   const oldStore = resource[this.kResourceStore];
   // 修改當(dāng)前異步資源的上下文
   resource[this.kResourceStore] = store;
   // 在新的上下文中執(zhí)行傳入的回調(diào)函數(shù)
   try {
     return ReflectApply(callback, null, args);
   } finally {
     resource[this.kResourceStore] = oldStore;
   }
 }

回調(diào)函數(shù)里可以通過 asyncLocalStorage.getStore() 獲得設(shè)置的公共上下文。

getStore() {
  const resource = executionAsyncResource();
  return resource[this.kResourceStore];
}

getStore的原理很簡(jiǎn)單,首先拿到當(dāng)前的異步資源,然后根據(jù)AsyncLocalStorage的kResourceStore的值從resource中拿到公共上下文,如果是同步執(zhí)行g(shù)etStore(比如 handler1 中),那么executionAsyncResource返回的就是我們請(qǐng)求所對(duì)應(yīng)的異步資源,上下文就是在run時(shí)設(shè)置的上下文({req, res}),但是如果是異步getStore那么怎么辦呢?因?yàn)檫@時(shí)候executionAsyncResource返回的不再是請(qǐng)求所對(duì)應(yīng)的異步資源,也就拿不到他掛載的公共上下文。為了解決這個(gè)問題,Node.js對(duì)公共上下文進(jìn)行了傳遞。

const storageList = []; // AsyncLocalStorage對(duì)象數(shù)組
const storageHook = createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    const currentResource = executionAsyncResource();
    for (let i = 0; i < storageList.length; ++i) {
      storageList[i]._propagate(resource, currentResource);
    }
  }
});

 _propagate(resource, triggerResource) {
    const store = triggerResource[this.kResourceStore];
    resource[this.kResourceStore] = store;
  }

我們看到在每次資源創(chuàng)建的時(shí)候,Node.js會(huì)把當(dāng)前異步資源的上下文掛載到新創(chuàng)建的異步資源中。所以在asyncLocalStorage.getStore() 時(shí)即使不是我們?cè)趫?zhí)行run時(shí)創(chuàng)建的資源對(duì)象,也可以獲得具體asyncLocalStorage對(duì)象所設(shè)置的資源( handler2 中)。關(guān)系圖如下。

這樣就實(shí)現(xiàn)了異步資源上下文的共享和隔離。

總結(jié)

AsyncLocalStorage 有很多用法和用處,我們?cè)?Node.js APM 中也大量用到該技術(shù),通過 AsyncLocalStorage,我們可以無侵入地實(shí)現(xiàn)對(duì) Node.js 應(yīng)用的內(nèi)部進(jìn)行觀測(cè),時(shí)間關(guān)系,本文簡(jiǎn)單地介紹了 AsyncLocalStorage 的使用和原理,有興趣的同學(xué)可以自行探索。


網(wǎng)站名稱:穿針引線之AsyncLocalStorage
轉(zhuǎn)載來源:http://m.5511xx.com/article/cdhhiep.html