NodeバインディンググローバルTraceIDの実現方法
5931 ワード
問題の説明
Node.jsのために 単スレッドモデルの制限は、グローバルトラッキングをセットすることができません。 出力ログと要求の結合を実現します。ログと要求のバインディングが実現されないと、ログ出力と対応するユーザ要求との対応関係を判断するのは難しいです。 オンライン問題の調査は困難をもたらした。
例えば、ユーザにアクセスする retrieveOne APIの場合、その呼び出しがあります。 retrieveOne Sub関数は、もし私達が retrieveOne Sub関数から現在の要求に対応する学生情報を出力するのは、煩雑です。course-seの既存の実現の下で、私達の針はこの問題に対する解決方法は:案1:呼び出し中 retrieveOne Sub関数の親関数、すなわち retrieveOne内で、はい。 paramDataで行います 学生に関する情報を出力するが、この案は ログを細かくして粒度を出力できませんでした。 案2:修正 retrieveOne Sub関数署名、受信 paramDataはパラメータです。 ログ出力粒度を確保できますが、 チェーンが深い場合は、関数ごとに署名を変更して、それを受信する必要があります。 paramDataさん、仕事量が多くて、あまりできません。
実は、以上の問題に対して、NodeのAsync Hook s実験的なAPIから入手できます。Node.js v 8.xの後、公式提供しました。 非同期行為を傍受するAsync Hook(非同期フック)APIのサポート。
Async Scope
Async Hooksは、それぞれの(同期または非同期)関数に対してAsync Scopeを提供しています。呼び出し可能です。 execution AsyncIdメソッドは、現在の関数のAsync IDを取得し、呼び出します。 trigger AyncIdは、現在の関数の使用者のAync IDを取得する。は、関数f内のAsync IDとその使用者のAync IDと同じように、同期関数を呼び出します。 の同じ関数は、異なるタイミングで非同期呼出しされ、上記のコードのg関数のような異なるAsync IDに割り当てられます。 非同期のリソースを追跡
前に述べたように、Aync Hookは非同期リソースを追跡するために使用できます。この目的を達成するためには、Async Hooksの関連APIを理解し、以下のコードのコメントを参照して具体的に説明する必要があります。
新たな解決策
Async Hooks APIに基づいて、以下の解決策を設計することができます。ログと記録を要求するバインディング、すなわちTrace IDのグローバルバインディングを実現します。
このようにして、コンテキストデータを最初に設定するだけで、コンテキストデータを取得し、問題を解決します。
上記のコードを実行し、その結果は以下の通りです。出力ログによると、私たちの解決策は実行可能です。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。
Node.jsのために 単スレッドモデルの制限は、グローバルトラッキングをセットすることができません。 出力ログと要求の結合を実現します。ログと要求のバインディングが実現されないと、ログ出力と対応するユーザ要求との対応関係を判断するのは難しいです。 オンライン問題の調査は困難をもたらした。
例えば、ユーザにアクセスする retrieveOne APIの場合、その呼び出しがあります。 retrieveOne Sub関数は、もし私達が retrieveOne Sub関数から現在の要求に対応する学生情報を出力するのは、煩雑です。course-seの既存の実現の下で、私達の針はこの問題に対する解決方法は:
/**
*
* @param {ParamData} paramData
* @param {Context} ctx
* @param {string} id
*/
export async function retrieveOne(paramData, ctx, id) {
const { subModel } = paramData.ce;
const sub_asgn_id = Number(id);
// paramData.user user , user_id ,
// , retrieveOneSub ,
// paramData 。
const { user_id } = paramData.user;
console.log(`${user_id} is trying to retreive one submission.`);
// retrieveOneSub 。
const sub = await retrieveOneSub(sub_asgn_id, subModel);
const submission = sub;
assign(sub, { sub_asgn_id });
assign(paramData, { submission, sub });
return sub;
}
/**
*
* @param {number} sub_asgn_id
* @param {SubModel} model
*/
async function retrieveOneSub(sub_asgn_id, model) {
const [sub] = await model.findById(sub_asgn_id);
if (!sub) {
throw new ME.SoftError(ME.NOT_FOUND, ' ');
}
return sub;
}
Aync Hook s実は、以上の問題に対して、NodeのAsync Hook s実験的なAPIから入手できます。Node.js v 8.xの後、公式提供しました。 非同期行為を傍受するAsync Hook(非同期フック)APIのサポート。
Async Scope
Async Hooksは、それぞれの(同期または非同期)関数に対してAsync Scopeを提供しています。呼び出し可能です。 execution AsyncIdメソッドは、現在の関数のAsync IDを取得し、呼び出します。 trigger AyncIdは、現在の関数の使用者のAync IDを取得する。
const asyncHooks = require("async_hooks");
const { executionAsyncId, triggerAsyncId } = asyncHooks;
console.log(`top level: ${executionAsyncId()} ${triggerAsyncId()}`);
const f = () => {
console.log(`f: ${executionAsyncId()} ${triggerAsyncId()}`);
};
f();
const g = () => {
console.log(`setTimeout: ${executionAsyncId()} ${triggerAsyncId()}`);
setTimeout(() => {
console.log(`inner setTimeout: ${executionAsyncId()} ${triggerAsyncId()}`);
}, 0);
};
setTimeout(g, 0);
setTimeout(g, 0);
上記のコードの中で使用します。 set Timeoutは非同期呼出しプロセスをシミュレートし、非同期プロセスではhandler同期関数を呼び出して、それぞれの関数で対応するAsync IDとTrigger Aync IDを出力します。上記のコードを実行したら、その運転結果は以下の通りです。
top level: 1 0
f: 1 0
setTimeout: 7 1
setTimeout: 9 1
inner setTimeout: 11 7
inner setTimeout: 13 9
上記ログ出力により、以下の情報が得られます。前に述べたように、Aync Hookは非同期リソースを追跡するために使用できます。この目的を達成するためには、Async Hooksの関連APIを理解し、以下のコードのコメントを参照して具体的に説明する必要があります。
const asyncHooks = require("async_hooks");
// AsyncHooks 。
const hooks = asyncHooks.createHook({
// init 。
init: function(asyncId, type, triggerId, resource) {},
// before 。
before: function(asyncId) {},
// after 。
after: function(asyncId) {},
// destroy 。
destroy: function(asyncId) {}
});
// hooks 。
hooks.enable();
// 。
hooks.disable();
私たちは呼び出し中です createHookの場合は、注入できます。 init、 before、 アフターと destroy関数は、 非同期資源の異なるライフサイクルを追跡する。新たな解決策
Async Hooks APIに基づいて、以下の解決策を設計することができます。ログと記録を要求するバインディング、すなわちTrace IDのグローバルバインディングを実現します。
const asyncHooks = require("async_hooks");
const { executionAsyncId } = asyncHooks;
// 。
const contexts = {};
const hooks = asyncHooks.createHook({
// init 。
init: function(asyncId, type, triggerId, resource) {
// triggerId asyncId 。
if (contexts[triggerId]) {
// 。
contexts[asyncId] = contexts[triggerId];
}
},
// destroy 。
destroy: function(asyncId) {
if (!contexts[asyncId]) return;
// 。
delete contexts[asyncId];
}
});
// ! hooks 。
hooks.enable();
// 。
function handler(params) {
// context , ( Logger Middleware)。
contexts[executionAsyncId()] = params;
// 。
console.log(`handler ${JSON.stringify(params)}`);
f();
}
function f() {
setTimeout(() => {
// params 。
console.log(`setTimeout ${JSON.stringify(contexts[executionAsyncId()])}`);
});
}
// ( )。
setTimeout(handler, 0, { id: 0 });
setTimeout(handler, 0, { id: 1 });
上記のコードの中で、先に声明しました。 contextsは、各非同期プロセスにおけるコンテキストデータ(例えば、Trace ID)を保存するために使用され、その後、Aync Hooksのインスタンスを作成した。非同期リソースの初期化時には、現在のAync IDに対応するコンテキストデータを設定し、そのデータを使用者のコンテキストデータとします。非同期リソースが破壊された時、対応するコンテキストデータを削除します。このようにして、コンテキストデータを最初に設定するだけで、コンテキストデータを取得し、問題を解決します。
上記のコードを実行し、その結果は以下の通りです。出力ログによると、私たちの解決策は実行可能です。
handler {"id":0}
handler {"id":1}
setTimeout {"id":0}
setTimeout {"id":1}
ただし、注意が必要なのはAync Hooksです。 実験API、 一定の性能損失がありますが、Nodeの公式は生産が可能になるよう努力しています。したがって、 マシンリソースが十分な場合、このソリューションを使用して、性能の一部を犠牲にして、開発体験と交換します。以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。