とある開発にて
MQTTで接続している特定のIoTデバイスに、定期的にイベントを通知したいという要件がありました。
イベントの内容は、システム制御的なコマンドなので静的なものになります。
シンプルな要件です。
方針決定
今回の例で言うと、IoTデバイスがラズパイなので、当初はcron仕込んで定期実行させようかと思いました。
ただ、それだとスケジューリングを変更したい時にsshする必要があります。接続する必要がなければsshは極力したくありません。
また、IoTデバイスにcronが動くデバイスしか使えなくなります。
他にも、定期実行の責務をデバイスからはがせるのであればそれに越したことはありません。
ということで、クラウドベンダーにGCPを使っている理由もあって、Cloud Schedulerで実装することにしました。
実装
デバイスは、Cloud IoTにデバイス登録されており、MQTTで接続しています。そして、各デバイスへの通信は前段にAPI(GAE)が用意してあるので、Cloud Scheduler経由でAPI(GAE)を直接叩くだけで、既存の資産を活かしつつ最小限で実装できそうです。
なので、当初想定していた構成はこんなかんじでした。
Cloud Scheduler の設定はUnix Cron形式をサポートしているので、そんなに迷うことなく設定が完了。
テスト実行させみました。
あれ?うまく動かない...
デバッグしたところ、Cloud Schdulerから送信したリクエストボディ(JSON payload)がAPI側でうまくハンドルできてないようでした。
いろいろと調べていった所、APIに使っていたrestify
のbodyParserがリクエストのContent-Typeをチェックしており、その分岐においてbodyParserが設定されないことが原因でした。
ちなみに、Cloud Scheduler がGAEを呼び出したときのContent-Typeはapplication/octet-stream
でした。
Cloud Schedulerにリクエストヘッダーを設定する方法をいろいろ探したのですが、時間がなく探すことを諦め、作戦を変更することにしました。
ピボット
次の作戦は、Cloud SchedulerのターゲットにPub/Subを指定し、Functions経由でIoTデバイスにイベントを通知することです。
構成はこんなかんじ。
幸い、GAE側の実装が簡単に移行できるようにしてあった為、作戦を切り替えて10分もかからずに先程の問題を解決することができました。
まとめ
Cloud Schedulerのいいところは、スケジュール対象を切り替えるのが簡単なところと、スケジューラー自体をデバイスに依存させない疎結合さだと思います。
再試行やロギングなども全部クラウド側で面倒みてくれるので、定期実行させるようなものは外から叩けるようにしてCloud Schedulerを使うのがオススメです。
ただ、Cloud Schedulerでヘッダーを自由に設定することができないのは解決していないので、だれか解決方法を知ってたら教えて下さい。