--
--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Tag:

13
2017

Zone.jsをNode.js版ThreadLocalとして使う

CATEGORYJavaScript
Node.js でアプリ作ってて、リクエスト情報(セッションIDとかIPアドレスとか)をログとかで出したいけど、そのためにモデル層にリクエストを引き渡したくない、という事で方法を調べたところ、Zone.js というものが使えそうという事が分かったのでその話。

こういう仕組みを作る場合、グローバル変数的なものがあればそこに置いておけばいいんだけど、JavaScriptは非同期処理なので普通のグローバル変数では駄目で、リクエストごとに一意な何かが必要となる。
(PHPみたいにリクエストごとにインスタンスが破棄される言語なら普通のグローバル変数で十分だけど。)
他のマルチスレッド系の言語だと、例えばJavaのサーバーサイドには、スレッドごとに一意なグローバル変数的な存在の ThreadLocal というものがあるので、Node.jsにもこういう仕組みがないか探したところ、標準ではないけどZone.jsというライブラリを使えばできそうな事が分かった。
Node.js標準のdomainも似た機能らしいけど、6現在deprecated。)

Zone.js

Zone.js はAngularの開発チームが作ってるライブラリだそうで、Dart(JavaScript代替言語の一つ)にある同名の仕組みをJavaScriptでやろうとしたものらしい。
簡単に説明すると、Zone というものを作成して、そのZoneのrunメソッド経由で関数を実行すると、その中がスコープみたいになって、Zoneに登録したプロパティやらを参照できるというもの。

凄いことに、runメソッドの内部で実行されたものであれば、非同期処理からであっても親のZoneを参照できる。
なんかアスペクト指向的に埋め込んでる?っぽい雰囲気。

その他、非同期処理内で発生したエラーなども、onHandleErrorのイベントでキャッチしたりできて、元々はそっちの用途がメイン?
でも今回はグローバル変数的な使い方を紹介してく。

使い方

一番単純な使い方はこんな感じ。
require('zone.js');

Zone.current.fork({
name: 'xxxZone',
properties: {
str: 'test',
},
}).run(() => {
console.log(Zone.current.get('str'));
});
はい、コードの意味は分かりやすいけど、単純すぎて便利さが伝わり難いコードですね(^^;
という事で、実際の事例に近いサンプルも。
require('zone.js');
const log4js = require('log4js');
const logger = log4js.getLogger('debug');

function handleRequest(request, response, next) {
Zone.current.fork({
name: 'requestInfoZone',
properties: {
user: request.user,
ipAddress: request.ip,
},
}).run(next);
}

function sendLog(message) {
const zone = Zone.current;
const user = zone.get('user');
const ipAddress = zone.get('ipAddress');
logger.debug(`userId=${user.id},ipAddress=${ipAddress},message=${message}`);
}

module.exports = {
handleRequest: handleRequest,
sendLog: sendLog,
};
Expressで使うイメージ。リクエストハンドラーでZoneを作成しておいて、後は普通にログ出力関数を呼べば、Zoneに保存されているユーザーIDやらIPアドレスやらが取れる。
今回みたいな目的の場合、こうやってリクエストハンドラーにすると処理のどこからでも参照できて凄く使いやすい。

リクエストハンドラーで割り込むやり方は、前述のエラー処理や、応用してトランザクションなんかの仕組みを作る場合も使えるのでおススメ。
(runで呼び出した関数がいつ終わるかは直接わからない?けど、responseのfinishイベントとかで代替できるし。)

注意点

Zoneはどうもアスペクト的に処理を埋め込むっぽいので、エラーログのスタックトレースが長くなる。。。

また、困ってるのが2017年1月現在のバージョンには、PromiseライブラリのBluebird(Sequelizeが使ってる)との相性問題?があること。
Bluebird製のPromiseのエラーがキャッチできない?とか、そもそもBluebirdが先にrequireされるとBluebird製のPromiseがZone.jsに認識されないとか、遭遇してしまうと回避が難しそうなので辛い(><

後は、類似ライブラリ(前述のdomainとか、StrongLoop zoneとか)がちょくちょく開発停止してて、なんか技術的に難しい部分があるのかなーこれは大丈夫かなーというのが心配。
その仕組み上システムの深いところに組み込むことになりがちなライブラリだと思うので、致命的なバグが見つかって放置とかされると影響が大きい。。。
ただ、Angular2がzone.jsをベースにしてるようなので、とりあえずAngular2が続く間はサポートされそうかなといのは、心強い。


以上、問題点はいくつかあるものの、とはいえかなり便利なライブラリなので、同じような悩みに遭遇した方は試してみるといいと思うよ('ω')ノ
スポンサーサイト

Tag: JavaScript Node.js Zone.js

0 Comments

Leave a comment

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。