AWS IoT CoreでProtocol Buffers(protobuf)を使うときに注意する点

AWS IoT CoreでProtocol Buffers(protobuf)を使うときに注意する点

Clock Icon2025.01.10

こんにちは。製造ビジネステクノロジー部のakkyです。

AWS IoT CoreではProtocol Buffers形式(protobuf)でエンコーディングされたメッセージをJSONへデコードできます。

Protocol Buffersを使用すると、メッセージが事前に定義したスキーマに適合していることを保証できるので、クライアント-サーバ間が分離されていてプログラム言語も異なるようなMQTTのユースケースではありがたみがありますし、なによりバイナリ化することでメッセージの転送サイズを削減することができるので、特に従量課金のインターネット回線を利用しているIoT機器では恩恵が大きい機能です。

一方で、Protocol Buffersではいくつかの注意点がありますので、本記事では躓きやすい点をご紹介します。

Protocol Buffersの基本的な使い方は、公式ドキュメントや、公式ブログの以下の記事などをご覧ください。

https://aws.amazon.com/jp/blogs/news/how-to-build-smart-applications-using-protocol-buffers-with-aws-iot-core/

128KB制限は回避できない

Protocol Buffersでエンコーディングするとデータ量が削減できるため、IoT CoreのMQTTのクオータである128KBの制限を回避できると思うかもしれませんが、Protocol Buffersを使用しても128KBより大きなメッセージを送信することはできません。

これは、デコード後のサイズにも128KBのクオータが適用されるからです。
公式ドキュメントに「インバウンドペイロードの最大サイズは 128 KiB (1KiB = 1024 バイト)、アウトバウンドペイロードの最大サイズは 128 KiB」との記載があります。

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/binary-payloads.html#binary-payloads-protobuf

decode関数で指定するメッセージタイプには名前空間などを付けない

Protocol Buffersバイナリをそのまま(BASE64エンコードなどせず)送り、それをデコードする場合には、以下のようなクエリを書きます。

SELECT VALUE decode(*, 'proto', '<BUCKET NAME>', '<FILENAME>.desc', '<PROTO_FILENAME>', '<PROTO_MESSAGE_TYPE>') FROM '<MY_TOPIC>'

それぞれ以下のように指定します。

  • BUCKET NAME descファイルが含まれているS3バケットの名前
  • FILENAME descファイル名。拡張子を__付ける__。
  • PROTO_FILENAME descへ変換する前の.protoファイルのファイル名。拡張子.protoは除くtest.proto から test.desc を作成した場合には test を指定する。
  • PROTO_MESSAGE_TYPE メッセージタイプ名(後述
  • MY_TOPIC トピック名

拡張子をつける、付けないなどが混在していて混乱しますが、PROTO_MESSAGE_TYPEの指定にも注意が必要です。

Protocol Buffersには名前空間(package)という概念があり、package xxx`という構文で指定します。
注意点としては、PROTO_MESSAGE_TYPEには名前空間を__指定してはいけない__点があります。

メッセージタイプをmymsgとして、package mypkgを記述していても、mypkg.mymsgなどと書いてはだめで、mymsgとだけ書く必要があります。
メッセージタイプ(正式にはFileDescriptorSet)の名前は単体で指定しましょう。

名前空間を指定するとエラーとなり、以下のようなログが出ます。(IoT Coreでログ出力を有効にしたとき)

{
    "timestamp": "",
    "logLevel": "ERROR",
    "traceId": "",
    "accountId": "",
    "status": "Failure",
    "eventType": "FunctionExecution",
    "clientId": "",
    "topicName": "",
    "ruleName": "",
    "ruleAction": "Decode",
    "resources": {},
    "principalId": "",
    "reason": "FileDescriptorSet did not contain specified messageType 'xxxx.xxxx'",
    "details": "decode(..., 'proto', 'bucket_name', 'test.desc', 'test', 'xxxx.xxxx') failed while attempting decode"
}

プロパティ名がcamelCaseになる

.protoファイルにtest_propertyというプロパティを指定すると、decode関数でのデコード後にはtestPropertyという名前になります。
ドキュメントには記載がありませんが、snake_caseのプロパティ名がcamelCaseに変更されます。

どうしてもプロパティ名を変更したくない場合には、Protocol BuffersでデコードするルールからRepublishアクションを使用して一時処理用トピックを作成し、その一時処理用トピックでプロパティ名を変更するという2段構成にするといいでしょう(メッセージング課金は増加します)

構成例

  1. デコード用トピック decode関数でデコードした後、一時トピックへRepublishする
  2. プロパティ名変更トピック SELECT testProperty AS test_property FROM 一時トピックでプロパティ名を変更し、本来のアクションを行う

doubleが指数表記になる

不具合ではありませんが、人間が見た場合に違和感を覚える場合がある点です。
double型の値が(floatは未検証)指数表記になる場合があるようです(1.2345678E9という形式)
精度は保たれているので、実用上問題ないはずです。

Tips: CDKで設定する場合にはgrantReadで良い

これはTipsです。
.descファイルを配置するS3バケットへIoT Coreがアクセスできるようにするためのポシリーとして、ドキュメントではs3:Get*を指定していますが、CDKではgrantReadで付ければ問題ありませんでした。

(ただし、今後IoT Coreの仕様変更によって使用できなくなる可能性もありますのでご注意ください。)

s3bucket.grantRead(new iam.ServicePrincipal("iot.amazonaws.com"));

grantReadで付く権限については以下の記事をご覧ください。

https://dev.classmethod.jp/articles/aws-cdk-grant-method-one-line-permissions/

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.