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で実装できる。

