yu nkt’s blog

nkty blog

I'm an enterprise software and system architecture. This site dedicates sharing knowledge and know-how about system architecture with me and readers.

Node-RED v0.19のPersistable contextを使ってみた

イントロ

先日、Node-REDの新バージョンv0.19がリリースされたので、新機能を使ってみました。

今回試してみたのは、Persistable contextです。 Persistable contextとは、これまではメモリのみで管理されてきたglobalコンテキストやflowコンテキストと言った、大域変数的なものを、ファイルや外部のストレージに保存できる仕組みのことです。 この機能により、何らかのトラブルでNode-REDのプロセスが止まっても、コンテキストの情報が消滅する事を防げます。

Context browser

まず、Persistable contextの前に、Context browserというPersistable contextに付随する新機能に注目しましょう。 これは、コンテキストの値を表示する、エディター内のエリアのことです。 下の画面の右側です。

f:id:yunkt:20180817144054p:plain

このフローでは、Changeノードでflowコンテキストにmsg.payloadの値を入れています。 Injectノードを叩いた後に右側のFlowの更新ボタンを押すと、flowコンテキストの値を見ることが出来ます。

フローは、こちらの通りです。

[{"id":"f71cbc29.c580e","type":"inject","z":"1af50515.7fc843","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":120,"wires":[["fa489cb4.7685c8"]]},{"id":"fa489cb4.7685c8","type":"change","z":"1af50515.7fc843","name":"","rules":[{"t":"set","p":"test","pt":"flow","to":"payload","tot":"msg"},{"t":"set","p":"test","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":120,"wires":[["57b6b2bc.8879ec"]]},{"id":"57b6b2bc.8879ec","type":"debug","z":"1af50515.7fc843","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":490,"y":120,"wires":[]}]

これまではエディターではコンテキストの値を確認することができませんでした。 そのため、コンテキストの値を確認するためには、ChangeノードかFunctionノードを利用してmsg内にコンテキストの値を入れ、Debugノードで表示する、といった手間が必要でした。 Context browserによって、そのような確認用のフローを設置する必要がなくなるため、デバッグのためだけに必要な余計な処理を、記述したり除去したりしなくてすみます。 printfデバッグから、デバッガーに進歩した印象です。

Persistable contextの有効化

次に、Persistable contextです。 まずはPersistable contextをenableにするために、settings.jsを修正しましょう。

$ vim $HOME/.node-red/settings.js

そして、以下の行を下のように編集します。

    // Context Storage
    // The following property can be used to enable context storage. The configuration
    // provided here will enable file-based context that flushes to disk every 30 seconds.
    // Refer to the documentation for further options: https://nodered.org/docs/api/context/
    //
    contextStorage: {
        default: {
            module:"localfilesystem"
        },
    },

localfilesystemに設定することで、ファイルにコンテキストの情報を書き出すようになります。

settings.jsの修正が終わったら、Node-REDを(再)起動して、修正を反映させましょう。

出力ファイルを確認

先ほどと同じフローのInjectノードをクリックすると、コンテキストの値がファイルに永続化されます。

このファイルは、$HOME/.node_red/contextディレクトリに存在します。 フローID (タブのID)と同じディレクトリが作られ、その中にフローコンテキストの情報を格納するファイルが生成されます。

centos:~/.node-red/context/1af50515.7fc843 $ cat flow.json
{
    "test": 1534504312108
}

ここでいうフローとは、Node-REDのタブの事です。 flowコンテキストは、一つのタブの中で共有されるので、タブ毎にコンテキスト情報が出力されます。

globalコンテキストは、contextディレクトリ直下にglobal.jsonが置かれました。 これは、Node-RED毎にglobalコンテキストは一つだからでしょう。

その他、詳細はこちらの通りです。

Node-RED : Local Filesystem Context Store

Context storeプラグイン

Persistable contextの重要なポイントは、どこにコンテキストの値を外部出力するかを、プラグインとして、Node-REDに追加で設定できる点です。 そのため、ビルトインでは、メモリへの格納とファイルへの永続化のみが用意されていますが、任意のDBへのデータ格納などを、ユーザーが自分で作って使うことが出来ます。 このプラグインを、Context storeプラグイン、もしくはContext storeモジュールと呼びます。

このドキュメントは、こちらに記載されています。

Node-RED : Context Store API

実装しなければならないAPIが多いので、今回は、localfilesystemをパクったlocalfilesystem2を用意してみましょう。 localfilesystemは、GitHub上で、以下のディレクトリにコードがあります。

github.com

Persistable contextプラグインの開発

実装手順は、本家のガイドラインには書かれていませんが、簡単です。

まず任意のディレクトリを作成して、npmのプロジェクトを作成しましょう。

$ mkdir localfilesystem2; cd $_
$ npm init

プロジェクト名は、localfilesystem2にします。 エンドポイントは、index.jsでも構いません。

次に、index.jsを作成し、その中に、上のGitHublocalfilesystem.jsの中身をコピーしましょう。

このままでは、localfilesystem.js内で参照している別のライブラリの依存関係が解決出来ません。

var fs = require('fs-extra');
var path = require("path");
var util = require("../../util");
var log = require("../../log");

var safeJSONStringify = require("json-stringify-safe");
var MemoryStore = require("./memory");

依存しているモジュールは、上記の通りですが、util, log, MemoryStoreはNode-RED内の別のコードなので、今は手っ取り早く、コメントアウトしましょう。 また、これらのモジュールを利用している箇所もコメントアウトしておきます。

残りのfs-extra, path, json-stringify-safeは、npmでインストールしましょう。

$ npm install --save-dev fs-extra path json-stringify-safe

Node-REDへの反映

あとは、作ったContext storeプラグインを、Node-REDに反映させるだけです。

手順は、独自ノードの開発とほぼ同じです。 Node-RED内部から、自作したモジュールをrequire出来るように、自作したモジュールへのリンクを張ります。

$ sudo npm link
$ cd $HOME/.node-red
$ npm link localfilesystem2

これで、Node-RED内部でlocalfilesystem2をrequire出来る状態になりました。 あとは、settings.jsを編集します。

    contextStorage: {
//        default: {
//            module:"localfilesystem"
//        },
        custom: {
            module:require("localfilesystem2")
        },
    },

確認作業の時に紛らわしいので、defaultのlocalfilesystemはコメントアウトしておきました。

Node-REDを起動し、Injectノードを押してみましょう。 期待通りに、$HOME/.node-red/context内に、コンテキストの情報が出力されている事が確認できると思います。

おわりに

Persistable contextには、他の特徴として、コンテキストの出力を同期的にも非同期的にも出来る、Changeノードだけでなくその他のノードでも利用出来る、などなどありますが、そこは割愛しました。

Node-REDの非機能面も、少しずつ拡充されていっている印象です。