2016-02-20

loopback (nodejs) でバックグラウンドジョブを実行するぞ

loopbackでサーバーからバックグラウンドジョブを起動するサンプル。 子プロセスとして4つのバックグラウンドジョブを動作させる。

キャットファーム デモ

ソース:

https://github.com/yamorijp/catfarm

セットアップ&起動:

nodejs 5.6.xとnpmをインストールしてパスを通しておく。

$ git clone https://github.com/yamorijp/catfarm.git
$ cd catfarm-demo
$ npm install
$ npm start

仕様:

  • 飼い主は家が準備出来たら4匹の猫を連れてくる。
  • 飼い主は猫パンチを食らうとうめき声をあげ、猫をかわいがる。
  • 飼い主は猫が居なくなると、すぐに連れ戻す。
  • 猫はたまに「にゃー」と鳴く。
  • 猫は稀に「みゃー」と鳴く。
  • 猫は空腹になったら餌を催促し、飼い主に猫パンチを食らわす。
  • 猫はしばしばいなくなる。

実装:

/server/server.js

サーバー用のbootディレクトリを追加し、オプションで指定する。

var options = {
  "appRootDir": __dirname,
  "bootDirs": [path.join(__dirname, "boot", "server")]
};
boot(app, options, err => {
  if (err) throw err;

  // start the server if `$ node server.js`
  if (require.main === module)
    app.start();
});

/server/boot/server/catfarm.js

サーバーが起動したら、バックグラウンドジョブをprocess.forkで起動する。

app.on("started", () => {
  cats.forEach((args, i, arr) => restart(args));
});

function restart(args) {
  var child = fork(path.join(__dirname, '../../jobs/cat.js'), args);
  ...
  }  

ジョブからのメッセージは"message"イベントで受け取る。
ジョブへのメッセージ送信はsendメソッドを使う。ジョブが終了した場合は"close"イベントが発生する。

child
  .on("message", (msg) => {
    if (msg == 'punch') {
      console.log(`${master}: Ouch!`[color]);
      console.log(`${master}: ${args[0]}, wait a minutes.`[color]);
    }
    child.send("patting");
  })
  .on("close", () => {
    console.log(`${master}: Come back, ${args[0]}.`[color]);
    restart(args);
  });

server/jobs/cat.js

バックグランドジョブの実装。
最初にloopback-boot#bootをコールしてフレームワークを起動する。

boot(app, path.join(__dirname, '..'), function(err) {
  if (err) throw err;
  main();
});

猫の振る舞いを実装。
process.sendで親プロセスにメッセージを送信。 process.exit()でプロセス終了。

function main() {
  ...

  setInterval(() => {
    // 稀にいなくなる
    console.log(`-- ${name} lost. --`[color]);
    process.exit();
  }, random(20000, 50000));

  process.on("message", (msg) => {
    // 飼い主のいうことは全て無視
    console.log(`${name}: ...`[color]);
  });

}

メモ

  • jobの最初にloopback-boot#bootをコールする。boot完了後からapp.modelsでモデルオブジェクトを参照できるようになる。
  • ブートディレクトリはサーバー用に別途用意し、デフォルトの/bootはジョブと共用する。bootDirsオプションを指定しても必ず/boot内のスクリプトをrequireするようハードコードされている。
  • jobは別プロセスで動作するのでモデルを編集してもサーバー側には通知されない。通知はprocess.send(message)pubsubで実装できる。

参照

loopback nodejs