Ho Ho Ho こんばんは、ソラコムでサンタ エンジニアをやってます松井(ニックネーム: moto)です。
ソラコムサンタ2021でお届けした、バイナリーパーサーの MessagePack 形式対応について、ちょっと深堀りしてお伝えしたいと思います。
今年の3月ごろに、ソーシャル上でこんなリクエストをいただいていました。
ここであらためてバイナリーパーサーと MessagePack 形式についておさらいをしてみましょう。
バイナリーパーサーとMessagePack
まずバイナリパーサーですが、IoT デバイスから送信されたバイナリデータをお客様が定義したフォーマットに従って SORACOM プラットフォーム上で JSON 形式にデコードし SORACOM Beam / Funnel / Funk / Harvest に送信できる機能です。
そしてMessagePackですが、公式サイトによれば
MessagePackは、効率の良いバイナリ形式のオブジェクト・シリアライズ フォーマットです。JSONの置き換えとして使うことができ、様々なプログラミング言語をまたいでデータを交換することが可能です。しかも、JSONよりも速くてコンパクトです。例えば、小さな整数値はたった1バイト、短い文字列は文字列自体の長さ+1バイトでシリアライズできます。
と説明があります。バイナリ形式ではあるものの、JSON のような階層構造を持つデータをシリアライズでき、さらに JSON よりもコンパクトなため IoT 用途には非常に向いてそうですね。
SORACOM と MessagePack のこれまで
実はあらかじめ送信するフォーマットが決まっているのであれば、バイナリーパーサーのカスタムフォーマットでもいけなくはないですし、SORACOMプラットフォーム内でデータ変換処理をプログラミングできる「SORACOM Orbit」 を使うという対応方法もありました。
しかし、特に開発の初期段階などで試行錯誤をしているような段階において、送信データのフォーマットを変えたい時にカスタムパーサーの設定をいちいち変えるのは面倒ですし、Orbit はデータのパース(解析)だけでなくデータの処理に使いたいですね。
ということで、今回新たにバイナリーパーサーが MessagePack 形式に対応しました。
MessagePack パーサーの設定方法
使い方は非常に簡単です。
SORACOMユーザーコンソール上のグループ設定の SORACOM Air 設定からバイナリパーサーを有効にして、フォーマットの欄には @msgpack
と入力します。
この設定だけで、IoTデバイスから送信された MessagePack 形式のデータはSORACOMプラットフォーム上で JSON 形式 へ変換され、SORACOMの各種サービスで扱うことが出来ます。先に紹介した通り、MessagePack自体が構造化されているため、この定義だけでJSON変換できるわけです。
それでは実際にデータを送信してテストを行ってみましょう。ここでは、Wi-Fi等のIPネットワークからSORACOMプラットフォームに接続できる「SORACOM Arc」 を用いて、すでに SORACOM につながっている Mac からデータを送信してみます。
msgpack は非常に多くの言語が対応していますが、ここでは Ruby でサクッと msgpack 形式のデータを作って、nc(netcat) コマンドを使って UDP 通信で Unified Endpoint に投げてみます。
~/work/msgpack$ cat gen_msgpack.rb require 'msgpack' # gem install msgpack msg = {"foo":"bar","a":1,"b":-2,"c":3.4}.to_msgpack print msg ~/work/msgpack$ ruby gen_msgpack.rb| nc -u uni.soracom.io 23080 201 #(Ctrl+C で止める)
これをデータ収集・蓄積サービスの「SOARCOM Harvest Data」で見てみると、
ちゃんと JSON 形式へ変換されていますね。ではデータのサイズを比較してみましょう。
~/work/msgpack$ ruby gen_msgpack.rb| od -vtx1 -Ad -a 0000000 84 a3 66 6f 6f a3 62 61 72 a1 61 01 a1 62 fe a1 84 � f o o � b a r � a soh � b � � 0000016 63 cb 40 0b 33 33 33 33 33 33 c � @ vt 3 3 3 3 3 3 0000026 ~/work/msgpack$ echo '{"foo":"bar","a":1,"b":-2,"c":3.4}' | wc -c 35
msgpackはバイナリデータなので直接表示ができません。そこでodコマンドで確認しています。msgpackは 26 bytes に対し、JSON 形式では 35 bytes でした。これだけで約30%程度の削減ができています!
MessagePack の活用シーン
Arduino などのマイコンを使ってセンサーデータを送信するようなケースでは、JSON を扱うための文字列処理が重荷になるケースがあります。そのような場合は、Arduino の MsgPack ライブラリを使って msgpack 形式でデータを扱うことでメモリや CPU リソースの節約となります。
実際に Arduino デバイスからデータを送信した例を見てみましょう。
コードはこちらに掲載しています (IoT スターターキット for Arduino 向けコードです)
送信データ例 (JSON 変換後)
{"merry":"christmas!","button":false,"rotaryEncoder":1023,"light":37,"temp":24,"humid":44,"pressure":1018.0599975585938,"accel":{"x":0,"y":0.10000000149011612,"z":-0.20000000298023224}}
先と同じように比較してみますが、ここでは追加で、送信に使うプロトコルによる違いも見てみます。
JSON 形式から msgpack 形式にすることで、単純に送信データの容量が半分以下となりました。先ほど以上の効果が出ていますが、これは対象のデータが階層構造を持つ複雑な JSON であることから、カッコやコンマやダブルクォートなどの文字数が大幅に削減できたからです。また桁数の多い数値データを送る場合も、削減効果が高いと思われます。
またプロトコル HTTP から TCP にするだけで不要な HTTP サーバヘッダーなどがなくなるため、データ容量が少なくなりました。さらに通信量をm削減したいのであれば、TCP のハンドシェーク処理などによるプロトコルオーバーヘッドすら削減する方法として、UDP で送信するのが最も効率がよくなります。(グラフの一番右)
IoT スターターキット for Arduino内のArduino 向け LTE-M 拡張ボード「LTE-M Shield for Arduino」で利用している 通信ライブラリTinyGSM は現在のところ UDP 通信をサポートしていませんが、搭載モデムである Quectel BG96 自体はATコマンドを用いてUDPの扱いができます。UDP+msgpackの場合はHTTP+JSONから80%も削減できるので、相当効果が高いですね。
MessagePack 形式がベストアンサーか?
前述しましたが、開発の初期段階などで試行錯誤をしているような段階においては、デバイス側のプログラム変更だけでフォーマットを変更可能である MessagePack 形式は、素早くプロトタイピングをするために非常に有用となります。
MessagePack 形式はJSON形式と同じくデータのラベル名(JSONでいうキー名)を含んでいます。ここは文字列形式であるため、ラベル名が多い、もしくはラベル名が長くなる場合はMessagePackと言えども削減効果は低くなります。
バイナリパーサーのカスタムフォーマットでは、SORACOM側に設定するフォーマット内にラベル名を付与できます。これにより、デバイスからは “ラベル無しのデータのみ” が送信できるため、さらならデータ容量の圧縮につながります。
Sigfox などデータ長の制限が厳しく、1ビットも無駄にしたくないケースでは、引き続きカスタムフォーマットでの利用がよいと思いますし、送信データのフォーマットがある程度決まってきたら独自のバイナリフォーマットを定義してカスタムパーサーの設定にラベル名を持たせることで、クラウド側の処理との辻褄をあわせつつデータ通信容量の最適化を行うとよいのではないでしょうか。
さいごに
ソラコムではお客様のフィードバックを受け常にプラットフォームを使いやすく進化させています。
ぜひ Twitter のハッシュタグ #ソラコムサンタ やフォームを通じて、機能追加や改善のリクエストをお寄せください。
それでは、また来年! Ho Ho Ho 🎅
― ソラコムサンタ 松井(moto)