---
title: "AWS IoT Coreのメッセージキューイングとは？共有サブスクリプションでQoS1を保持する条件と上限"
url: "https://www.issoh.co.jp/tech/details/8039/"
published: 2025-08-05
updated: 2026-11-06
categories: ["AWS"]
publisher: "株式会社一創"
---

# AWS IoT Coreのメッセージキューイングとは？共有サブスクリプションでQoS1を保持する条件と上限

AWS IoT Coreのメッセージキューイングは、MQTTの共有サブスクリプション（`$share/`で始まる購読）を組んだバックエンドのワーカーが全員オフラインになったとき、QoS1のメッセージをブローカー側に溜めておき、復帰後に配る機能です。2025年7月31日に追加され、MQTT 3.1.1と5の両方で、AWS IoT Coreが提供されている全リージョンで使えます。

名前から「デバイスが切断されたときのキュー」と受け取られがちですが、個々のクライアント向けの保持は以前からある永続セッションの機能で、2025年の追加は共有サブスクリプションのグループ向けです。この記事では、AWSの開発者ガイドとクォータ表（2026年11月時点）をもとに、キューが働く条件、保持期間と上限、paho-mqttとAWS IoT Device SDK v2でのワーカーの実装、監視、Amazon SQSへ逃がす判断までを整理します。AWS IoT Core自体の機能と料金の全体像は[AWS IoT Coreの料金・MQTT接続・はじめ方の解説](/tech/details/2991/)に、MQTTというプロトコル全般の仕組みは[MQTTのPub/Subの仕組みとMQTT 5.0の解説](/tech/details/15819/)にまとめています。

## まとめ：メッセージキューイングの要点

- **対象は共有サブスクリプション**：グループのメンバーに永続セッションが1つでもあれば、キューは自動で有効になる。オン・オフを切り替える設定やAPIは無い。
- **溜まるのはQoS1だけ**：メンバーが全員オフライン、または受信側が接続あたり毎秒100件の上限を超えたときにキューへ入る。QoS0は失敗時に捨てられる。
- **保持期間は既定1時間**：永続セッションの有効期限（既定3,600秒・引き上げ申請で最大7日）に従い、MQTT 5のメッセージ有効期限（最大604,800秒）を付けた場合はそちらが優先される。
- **上限を超えると新しいメッセージから捨てられる**：アカウントあたり毎秒500件、グループあたり約50万件（東京リージョンの既定値）。古いメッセージを押し出して入れ替えることはしない。
- **復帰後の払い出しはメンバー1台あたり毎秒20件まで**：溜まった件数が多いと、ワーカーを増やさない限り消化に時間がかかる。
- **SDK v2は既定のままだと毎回クリーンセッション**：AWS IoT Device SDK v2の`session_behavior`の既定はCLEAN。再起動したワーカーにセッションを引き継がせるには`REJOIN_ALWAYS`を指定する。
- **1時間を超える停止や失敗時の再試行が要るデータはSQSへ**：ルールエンジンからSQSへ送れば最大14日保持できる。キューイングはワーカーの再起動やデプロイ中の取りこぼし対策と割り切る。

## メッセージキューイングの対象範囲｜共有サブスクリプションと永続セッション

### 2025年7月31日に追加された機能の中身

[AWSの発表（What’s New、2025年7月31日）](https://aws.amazon.com/about-aws/whats-new/2025/07/aws-iot-core-message-queuing/)は、共有サブスクリプションのグループのメンバーが全員切断されたとき、または受信量を処理しきれないとき、永続セッション上のQoS1メッセージを自動でキューに入れる、と説明しています。キューに入ったメッセージは、メンバーが再接続したとき、またはグループに新しいメンバーが加わったときに配られます。課金は通常のメッセージングと同じ単価です。

共有サブスクリプションそのものは、同じトピックを複数のクライアントで分け合い、1件のメッセージをグループ内のどれか1台だけにランダムに届ける仕組みで、[2023年4月に一般提供](https://aws.amazon.com/about-aws/whats-new/2023/04/aws-iot-core-mqtt5-shared-subscriptions-cloudwatch-metrics/)されました。開発者ガイドは、1,000台のデバイスが同じトピックに送り、10個のバックエンドアプリケーションで処理を分担する例を挙げています。キューイングはこの「分担する側」が止まったときの穴を埋めるための追加です。QoSのレベルごとの違いは[MQTT QoSの0・1・2の違いと選び方](/tech/details/3611/)で解説しています。

### 以前からある永続セッションの保持との違い

個々のクライアントが永続セッションで接続していれば、切断中に届いたQoS1メッセージは以前から保持され、再接続時に配られていました。2つの保持は、誰宛てのメッセージか、誰が受け取るかが違います。

| 項目      | 永続セッションの保持               | 共有サブスクリプションのキュー               |
| ------- | ------------------------ | ----------------------------- |
| 保持の単位   | クライアントID（セッション）ごと        | 共有グループ（ShareName＋トピックフィルター）ごと |
| 主な受け手   | デバイス                     | バックエンドのワーカー群                  |
| 配り先     | 同じクライアントIDの再接続時のみ        | 復帰したメンバーか、新しく加わったメンバー         |
| 有効になる条件 | そのクライアントが永続セッション         | メンバーの1つ以上が永続セッション             |
| 保持期間の基準 | セッション失効で破棄。MEIによる期限切れも適用 | 既定3,600秒。MEI指定時はその値が優先        |

新しいワーカーが加わっただけでもキューが流れ出す点が、永続セッションの保持との実務上の差です。落ちたワーカーと同じクライアントIDで復旧させなくても、別のホストで立ち上げたワーカーが溜まった分を引き取れます。

## キューに入る条件と配信の流れ

### キューが有効になる条件

[AWS IoT Coreの開発者ガイド（MQTT）](https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html)の条件は1つだけで、共有グループに永続セッションのメンバーが少なくとも1つあることです。グループ全員がクリーンセッションならキューは働かず、全員が落ちた時点でメッセージは失われます。

永続セッションの定義も同じページにあります。MQTT 3.1.1ではCONNECTのcleanSessionフラグを0にし、MQTT 5ではClean Startを0、Session Expiry Intervalを0より大きくします。MQTT 5でSession Expiry Intervalを省略すると0として扱われ、切断と同時にセッションが消えます。

グループへの出入りも、セッションの種類で変わります。クリーンセッションのメンバーは切断した時点でグループから抜けます。永続セッションのメンバーは切断してもグループに残り、オフラインのメンバーとして扱われます。グループから外れるのは、明示的に購読を解除したときか、切断後にセッションの有効期限が切れたときです。

### キューに入るメッセージと捨てられるメッセージ

キューに入るのは、共有グループ宛てのQoS1メッセージのうち、次のどちらかに当たるものです。

- グループにオンラインのメンバーが1つもいない
- 受信側のメンバーが、接続あたりのPublish上限（毎秒100件・引き上げ不可）を超えている

送信済みでPUBACKが返っていないQoS1メッセージも、メンバーが全員切断されればキューの対象になります。一方、QoS0のメッセージは失敗すればその場で捨てられます。AWS IoT CoreはQoS2に対応しておらず、QoS2でSUBSCRIBEしてもSUBACKが返りません。取りこぼしたくないトピックは、送信側も受信側もQoS1にそろえる必要があります。

### 復帰後の払い出し速度

溜まったメッセージは、オンラインのメンバー1台あたり毎秒20件までの速度で配られ、通常の購読で届くメッセージと並行して流れます。払い出し上限はオンラインのメンバー数に比例します。上限まで受信できている場合はワーカーの追加で上限を増やせますが、DB処理や通信が律速している場合は、その改善も必要です。ただし、1つの共有グループに入れるメンバーは100件まで（引き上げ不可）なので、払い出しの理論上の上限はグループあたり毎秒2,000件になります。

## 保持期間・上限・料金｜クォータ表で見る制約

### 保持期間を決める2つの設定

キューの保持期間は、既定では永続セッションの有効期限のクォータに従います。既定値は3,600秒（1時間）で、Service Quotasから引き上げを申請でき、MQTT 5のSession Expiry Intervalとして最大7日まで指定できます。クライアントがアカウントの上限を超える値を送った場合、AWS IoT Coreは調整後の値をCONNACKで返します。

送信側がMQTT 5のMessage Expiry Interval（メッセージ有効期限）を付けた場合は、セッションの有効期限に関係なくその値が保持期間になります。最小は1秒、最大は604,800秒（7日）で、上限を超える値は7日に丸められます。数分で価値が無くなる計測値は、有効期限を短く付けておけば復帰後に古い値を処理せずに済みます。

### キューに関わるクォータ一覧

[AWS General ReferenceのAWS IoT Coreのクォータ表](https://docs.aws.amazon.com/general/latest/gr/iot-core.html)のうち、キューに関わる行を抜き出しました。東京リージョンは表の「既定値」の列が適用され、一部リージョン（ストックホルム、パリ、香港、カナダ中部など）は低い値になります。

| クォータ名                                                   | 既定値       | 一部リージョン   | 引き上げ |
| ------------------------------------------------------- | --------- | --------- | ---- |
| Persistent session expiry period                        | 3,600秒    | 3,600秒    | 可    |
| Queued messages per second per account                  | 500件/秒    | 500件/秒    | 可    |
| Queued Messages per shared subscription group           | 約500,000件 | 約100,000件 | 可    |
| Publish requests per second per connection              | 100件/秒    | 100件/秒    | 不可   |
| Shared Subscriptions per group                          | 100       | 100       | 不可   |
| Shared Subscription groups per account                  | 100       | 100       | 不可   |
| Subscribe and unsubscribe requests per second per group | 100件/秒    | 100件/秒    | 不可   |
| Maximum Message Expiry Interval                         | 604,800秒  | 604,800秒  | 不可   |

アカウントあたり毎秒500件の上限は、オフラインの永続セッション向けの保持と共有グループのキューの両方で数えられます。デバイス側の永続セッションで大量に溜めている構成では、共有グループの分と枠を取り合います。接続あたり毎秒100件の上限にも、オフラインの永続セッション宛てのメッセージとSendDirectMessageで送ったメッセージが含まれると、クォータ表の説明に書かれています。

グループあたりのSUBSCRIBE・UNSUBSCRIBEが毎秒100件までという制限は、ワーカーを一斉に入れ替えるときに効きます。セッションを引き継げずに全台が購読し直す構成で、100台近いメンバーが同じ秒に再接続すると、この上限に当たります。

### 上限を超えたときの挙動

AWS IoT Coreの開発者ガイドは、上限到達前にキューへ保存されたメッセージを残し、上限を超える新着メッセージを破棄する挙動を明記しています。古いメッセージを押し出して新しいものを入れる動きはしません。最新の値ほど重要なテレメトリでは、停止が長引くほど「古い値だけが残り、新しい値が消える」状態になる点に注意が必要です。

### 停止時間ごとの試算

200台のデバイスが毎秒1件ずつ送り、4台のワーカーが共有グループで受けている構成を考えます。流入は毎秒200件で、アカウントの毎秒500件の上限には収まります。

- **30分停止**：200件×1,800秒で360,000件。グループ上限の約50万件に収まる。
- **45分停止**：540,000件となり、約50万件を超えた分の新しいメッセージが捨てられる。
- **復帰後の消化**：4台の払い出し上限は合計毎秒80件で、360,000件を配り切るには計算上最短75分かかります。ただし、これは配信中に期限切れが起きない条件の試算です。既定の保持期間は1時間なので、この条件のまま全件を回収できるとは限りません。ワーカーを100台まで増やせば毎秒2,000件で約3分。

4台構成で通常の流入（1台あたり毎秒50件）に払い出し（毎秒20件）が加わっても、接続あたり毎秒100件の上限には収まります。ワーカーを減らして1台あたりの流入が毎秒80件を超えると、払い出しと合わせて上限に達し、超えた分のQoS1メッセージはキューに入ります。

### キューに入ったメッセージの料金

キューに入ったメッセージも通常のメッセージ単価で課金されます。[AWS IoT Coreの料金ページ](https://aws.amazon.com/iot-core/pricing/)の東京リージョンのメッセージ料金は、月10億件までが100万件あたり1.20ドル（2026年11月時点）で、メッセージは5KB単位で数えられます（8KBなら2件）。個々のデバイスの永続セッションについては、再接続時に配られた保存済みメッセージがもう一度課金されると開発者ガイドに書かれており、有効期限を延ばすほど料金が増える可能性があるとも注意しています。

## 実装｜paho-mqttとAWS IoT Device SDK v2による共有グループのワーカー

### paho-mqtt 2系でのワーカー側の接続設定

この実装例では、永続セッション、QoS1での共有購読、ワーカーごとに一意で再起動後も変わらないクライアントIDを使います。固定のIDは同じセッションを再開するための指定で、共有グループのキュー自体は別のIDで新しく加わったメンバーにも配られます。MQTT 5ではClean Startを0にし、Session Expiry Intervalに0より大きい秒数を指定します。MQTT 3.1.1ならcleanSessionフラグを0にします。MQTT 3と5をまたいだセッションの再開はできないので、ワーカーのプロトコルをそろえておきます。

```
import json
import ssl
import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.properties import Properties

ENDPOINT = "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
CLIENT_ID = "backend-worker-1"          # メンバーごとに固定・一意
SHARED_FILTER = "$share/workers/factory/+/telemetry"

def on_connect(client, userdata, flags, reason_code, properties):
    print("connect:", reason_code, "session_present:", flags.session_present)
    if not flags.session_present:
        # セッションが無い（初回・失効後）ときだけ購読し直す
        client.subscribe(SHARED_FILTER, qos=1)

def on_message(client, userdata, msg):
    data = json.loads(msg.payload)
    save_to_db(data)                    # 冪等に書く（同じmsg_idは上書き）
    client.ack(msg.mid, msg.qos)        # 処理が終わってからPUBACKを返す

def save_to_db(data):
    # 動作確認用のダミー。本番では冪等なDB保存とコミット完了待ちに置き換える
    print("demo only (not persisted):", data)

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id=CLIENT_ID,
    protocol=mqtt.MQTTv5,
    manual_ack=True,
)
client.tls_set(
    ca_certs="AmazonRootCA1.pem",
    certfile="worker.cert.pem",
    keyfile="worker.private.key",
    tls_version=ssl.PROTOCOL_TLS_CLIENT,
)
client.on_connect = on_connect
client.on_message = on_message

props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 3600      # 永続セッション（秒）
client.connect(ENDPOINT, 8883, keepalive=60, clean_start=False, properties=props)
client.loop_forever()
```

このコードは、TLSの設定とエンドポイントをローカルに差し替えて、paho-mqtt 2.2.0（2026年11月4日公開）とMosquitto 2.1.2で動作を確かめています。初回はsession\_presentがFalseで購読し、切断中に送られた3件は再接続時にsession\_presentがTrueのまま、購読し直さずに受け取れました（AWS IoT Core固有のキュー上限や払い出し速度は、この環境では再現していません）。2.2.0はPython 3.7と3.8のサポートを外しており、次の版で3.9と3.10も外す予定と告知しています。

paho-mqttの既定の自動ACKは、on\_messageが戻った後にPUBACKを返します。この例のようにコールバック内で同期的に保存するなら、自動ACKでも保存後に返されます。`manual_ack=True`が効くのは、処理を別スレッドやジョブキューへ渡して先にコールバックを終える構成です。その場合は保存完了まで`client.ack()`を呼ばずにおけば、処理中にワーカーが落ちても未ACKのQoS1メッセージとしてブローカー側に残ります。後から非同期化しても保存完了後にACKを返せるよう、例では最初から手動ACKにしています。ただし、ACKを保留できる時間と件数には上限があります。[クォータ表](https://docs.aws.amazon.com/general/latest/gr/iot-core.html)によると、接続中のクライアントへの未ACKのQoS1メッセージは、MQTT 5の購読者なら最大960秒（16分）、MQTT 3なら最大1時間再送された後に破棄されます。また、未ACKの送信が1クライアントあたり100件に達すると、ACKが返るまで次のメッセージは送られません。保存に時間がかかる処理では、ACKを溜め込まずに順次返す設計が必要です。同じメッセージが複数回届く場合に備え、送信側がペイロードに付けた一意なmsg\_idをDBの重複排除キーにします。MQTTのパケット識別子であるmsg.midは、そのキーには使いません。paho-mqtt 2系のコールバックの書き方と再接続の挙動は[paho-mqtt 2系のループ方式と再接続の解説](/tech/details/16866/)で詳しく扱っています。

### AWS IoT Device SDK v2でのワーカーとsession\_behaviorの指定

AWSのサンプルや共有サブスクリプションの入門記事は、AWS IoT Device SDK v2（Pythonならパッケージ名`awsiotsdk`）のMQTT 5クライアントで書かれています。このSDKでキューを働かせるときに外せないのが`session_behavior`です。SDKが依存する[aws-crt-pythonのmqtt5モジュール](https://github.com/awslabs/aws-crt-python/blob/main/awscrt/mqtt5.py)の定義では、既定の`DEFAULT`は`CLEAN`（接続のたびにクリーンセッションを要求）に対応します。`session_expiry_interval_sec`だけを指定しても、接続のたびにClean Start=1が送られ、前のセッションは終わります。

```
import json
import queue
from awscrt import mqtt5
from awsiot import mqtt5_client_builder

ENDPOINT = "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
CLIENT_ID = "backend-worker-1"          # メンバーごとに固定・一意
SHARED_FILTER = "$share/workers/factory/+/telemetry"
received = queue.Queue()

def on_success(data):
    if not data.connack_packet.session_present:
        # セッションを引き継げなかったときだけ購読し直す
        client.subscribe(mqtt5.SubscribePacket(subscriptions=[
            mqtt5.Subscription(SHARED_FILTER, mqtt5.QoS.AT_LEAST_ONCE)]))

def on_publish(data):
    if data.publish_packet.qos != mqtt5.QoS.AT_LEAST_ONCE:
        return                          # QoS0はACK制御の対象外（ハンドルを取得できない）
    # 自動PUBACKを止め、保存が終わるまでACKを保留する（awscrt 0.32.1以降）
    handle = data.acquire_publish_acknowledgement_control()
    received.put((data.publish_packet.payload, handle))

def save_to_db(record):
    # 動作確認用のダミー。本番では冪等なDB保存に置き換える
    print("demo only (not persisted):", record)

client = mqtt5_client_builder.mtls_from_path(
    endpoint=ENDPOINT,
    port=8883,
    cert_filepath="worker.cert.pem",
    pri_key_filepath="worker.private.key",
    ca_filepath="AmazonRootCA1.pem",
    client_id=CLIENT_ID,
    session_expiry_interval_sec=3600,
    session_behavior=mqtt5.ClientSessionBehaviorType.REJOIN_ALWAYS,
    on_publish_received=on_publish,
    on_lifecycle_connection_success=on_success,
)
client.start()
while True:
    payload, handle = received.get()
    save_to_db(json.loads(payload))
    client.invoke_publish_acknowledgement(handle)
```

セッションを引き継ぐ指定は2種類あり、ワーカーの再起動で差が出ます。awsiotsdk 1.31.0・awscrt 0.36.1で、ビルダーだけをローカルのMosquitto 2.1.2への平文接続に差し替え、ワーカーを止めている間に3件送ってから新しいプロセスで起動し直した結果が次の表です（AWS IoT Core実機ではなく、Mosquittoでのセッション引き継ぎの確認です）。

| session\_behavior     | 新しいプロセスの初回接続            | 停止中の3件 |
| --------------------- | ----------------------- | ------ |
| CLEAN（既定）             | session\_present: False | 届かなかった |
| REJOIN\_POST\_SUCCESS | session\_present: False | 届かなかった |
| REJOIN\_ALWAYS        | session\_present: True  | 3件とも受信 |

`REJOIN_POST_SUCCESS`は、同じプロセスの中で一度接続に成功した後の再接続でだけセッションを引き継ぎます。デプロイやコンテナの再起動で新しいプロセスになると、最初の接続はクリーンになります。AWS IoT Coreの共有グループのキューは新しく加わったメンバーにも配られる仕様ですが、Clean Start=1は前のセッションを終わらせるため、そのワーカー宛てに送信済みで未ACKだったメッセージの扱いは開発者ガイドに書かれていません。再起動をまたいで引き継ぐなら`REJOIN_ALWAYS`を選びます。awscrtの定義は、SDK側が未ACKのQoS1送信を永続化しないため、この指定は厳密にはMQTT仕様に沿わないと注記しています。ワーカーが自分から送るメッセージの再送までは保証されない、という意味です。

手動PUBACKは、aws-crt-pythonの[v0.32.1（2026年4月16日）](https://github.com/awslabs/aws-crt-python/releases/tag/v0.32.1)で入った機能です。`acquire_publish_acknowledgement_control()`はon\_publishのコールバックの中で1回だけ呼べ、戻った後に呼ぶとRuntimeErrorになります。呼ばなければ、コールバックが戻った時点でPUBACKが自動で返ります。QoS0の受信ではこの関数がNoneになるため、例では受信したQoSを先に確かめています。証明書の発行からSDK v2での接続までの手順は[ラズパイとAWS IoT Coreを接続する手順](/tech/details/2803/)で解説しています。

### デバイス側のMessage Expiry Interval設定

送信側では、QoS1で送ることに加えて、古くなった値を溜めないためのMessage Expiry Intervalを付けておくと、キューの上限を無駄に消費しません。

```
import json
import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.properties import Properties

def publish_telemetry(client, device_id, reading):
    props = Properties(PacketTypes.PUBLISH)
    props.MessageExpiryInterval = 600   # 10分を過ぎた値はキューから捨てる
    info = client.publish(
        f"factory/{device_id}/telemetry",
        json.dumps(reading),
        qos=1,
        properties=props,
    )
    return info
```

MQTT 5で接続したクライアントでのみ有効なプロパティです。MQTT 3.1.1のデバイスからは有効期限を付けられないため、その場合の保持期間はセッションの有効期限のクォータで決まります。

### IoTポリシーの書き方

iot:Subscribeはトピックフィルター（topicfilter）のリソースに対して、SUBSCRIBEのたびに評価されます。共有購読ではSUBSCRIBEで送る`$share/`から始まる文字列が評価対象になるため、許可するリソースも`$share/`から書きます。配信を受け取るiot:Receiveは、実際のトピック名（topic）のリソースです。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:ap-northeast-1:123456789012:client/backend-worker-*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:ap-northeast-1:123456789012:topicfilter/$share/workers/factory/+/telemetry"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "arn:aws:iot:ap-northeast-1:123456789012:topic/factory/*/telemetry"
    }
  ]
}
```

ポリシーの中では、MQTTのワイルドカードの`+`と`#`は特別な意味を持たない普通の文字として扱われ、ポリシーのワイルドカードは`*`と`?`です。上のiot:Subscribeは、ワーカーが送る`factory/+/telemetry`というフィルターの文字列そのものに一致させています。証明書認証とトピックACLの設計全体は[MQTTセキュリティのTLS・証明書認証・トピックACLの設計](/tech/details/16868/)を参照してください。

## 監視｜キューの深さと取りこぼしを見る指標

### キューの深さはAWS/UsageのApproximateQueueDepth

共有グループごとのキューの深さは、CloudWatchのAWS/Usage名前空間にあるApproximateQueueDepthで見られます。開発者ガイドが示している、グループごとのメトリクスを一覧するコマンドは次のとおりです。

```
aws cloudwatch list-metrics --namespace "AWS/Usage" \
  --dimensions Name=Resource,Value='ApproximateQueueDepth'
```

先の試算どおり、グループ上限（東京なら約50万件）の手前でアラームを置けば、捨てられ始める前にワーカーを増やす判断ができます。現在のクォータ値は、Service Quotasのコンソールでサービス「AWS IoT Core」を開くと確認できます。

### Queuedメトリクスとログによる保存成否の確認

[メッセージブローカーのメトリクス](https://docs.aws.amazon.com/iot/latest/developerguide/metrics%5Fdimensions.html)には、保存に成功した件数のQueued.Success、毎秒500件の上限で保存できなかった件数のQueued.Throttle、内部エラーで保存できなかった件数のQueued.ServerErrorがあります。メトリクスの説明文は永続セッションの切断中の保存として書かれているため、共有グループ単位の状況はApproximateQueueDepthと合わせて見ます。Queued.Throttleが0でない時間帯は、メッセージが実際に失われています。

AWS IoTのログを有効にしている場合は、CloudWatch LogsにeventTypeが`Queued`のエントリが出ます。失敗時のdetailsには`Throttled while queueing offline message`のような理由が入るので、どのクライアントIDとトピックで捨てられたかまで追えます。

### マネジメントコンソールでの動作確認

AWS IoTコンソールの［MQTT test client］で、2つのブラウザから同じ`$share/group1/topic1`を購読し、`topic1`へ数回送ると、メッセージがどちらか一方にランダムに届く様子を確認できます。キューの動作まで確かめるなら、前章のワーカーで購読成功を確認してから停止し、テストクライアントから`factory/device1/telemetry`へ有効なJSONをQoS1で数件送って、ワーカーを再起動します。停止中に送ったメッセージが再起動後に届いたかを、ペイロードのmsg\_idで照合します。QoS1では同じメッセージが重複して届くことがあるため、件数だけでなくmsg\_idで確かめ、ApproximateQueueDepthが0に戻ることも合わせて確認します。

## 使い分け｜共有グループのキューで足りる場面とSQSへ逃がす場面

共有グループのキューは、ワーカーの再起動、デプロイ、数十分の障害の間を埋めるための仕組みです。保持は既定1時間、件数も約50万件で頭打ちになり、配信の順序についての保証も開発者ガイドには書かれていません。共有グループはメッセージをメンバーにランダムに振り分けるため、メンバーをまたいだ処理順はそもそもそろいません。

| 方式                 | 保持          | 向く用途                    |
| ------------------ | ----------- | ----------------------- |
| 共有グループのキュー         | 既定1時間・約50万件 | ワーカーの短時間停止の穴埋め          |
| ルールエンジン→Amazon SQS | 最大14日       | 長時間停止・失敗時の再試行・DLQが要るデータ |
| Direct Messaging   | 保持しない       | オンラインの1台への指示と到達確認       |

判断の線引きははっきりしています。課金や監査に使う記録、1時間を超える停止があり得るシステム、処理失敗時の再試行が必要な用途は、MQTTの購読で受けるのをやめ、AWS IoTルールのSQSアクションでキューへ送ります。ただし、SQSで処理後に削除したメッセージは再取得できません。処理済みデータを後日再処理する場合は、S3などに原本を別途保存する構成が必要です。SQSのメッセージ保持期間は最大14日で、処理に失敗したメッセージは設定した再受信回数に応じてデッドレターキューへ移せます。なお、AWS IoTルールのSQSアクションはFIFOキューに対応せず、転送順序も保証しないため、送り先は標準キューになります。可視性タイムアウトとDLQの設計は[Amazon SQSの標準キューとFIFOの違いとDLQ設計](/tech/details/17241/)で解説しています。

[2026年5月に加わったDirect Messaging](https://aws.amazon.com/about-aws/whats-new/2026/05/aws-iot-core-direct-messaging/)は、クライアントIDを指定して1台に直接送る機能で、[開発者ガイド](https://docs.aws.amazon.com/iot/latest/developerguide/direct-messaging.html)はオフラインのデバイス向けにはキューしないと明記しています。「届かなかったら溜めておく」用途には使えないので、キューイングの代わりにはなりません。

キューイングを入れたのに取りこぼす失敗は、次の4つが典型です。

- **全メンバーがクリーンセッション**：MQTT 5でSession Expiry Intervalを指定しないと既定の0になり、切断と同時にセッションが消える。メンバー全員がこの状態だとキューは一度も有効にならない。
- **SDK v2のsession\_behaviorが既定のまま**：Session Expiry Intervalを付けても、接続のたびにClean Start=1で前のセッションが終わる。再起動をまたぐならREJOIN\_ALWAYSを指定する。
- **ワーカー間でクライアントIDが重複**：同じIDで接続すると先の接続が切られ、メンバー数が実際より少なくなる。コンテナを複製するときはIDにホスト名などを含める。
- **購読がQoS0**：開発者ガイドが保存対象として挙げるのは、QoS1で送られQoS1で購読されたメッセージ。送信側がQoS1でも、購読をQoS0にすると配信はQoS0扱いになる。

キューの上限・ワーカーの台数・SQSへの切り分けを含めたIoTデータ基盤の設計は、[AI/IoTソリューション](/service/ai/iot/)としてご相談いただけます。

## よくある質問

### AWS IoT CoreのメッセージキューイングはどのQoSに対応していますか？

キューに入るのはQoS1のメッセージだけです。QoS0は配信に失敗した時点で捨てられます。AWS IoT CoreはQoS0と1のみに対応しており、QoS2は使えません。

### メッセージキューイングを有効にする設定やAPIはありますか？

ありません。共有サブスクリプションのグループに永続セッションのメンバーが1つでもあれば、自動で有効になります。ワーカーをMQTT 5のClean Start=0とSession Expiry Interval>0、またはMQTT 3.1.1のcleanSession=0で接続するのが有効化の手順にあたります。AWS IoT Device SDK v2では`session_behavior`に`REJOIN_ALWAYS`を指定します。

### キューに入ったメッセージはどれくらい保持されますか？

既定では永続セッションの有効期限と同じ3,600秒（1時間）です。クォータの引き上げで最大7日まで延ばせます。送信側がMQTT 5のメッセージ有効期限を付けた場合は、その値（最大7日）が優先されます。

### 個々のデバイスが切断している間のメッセージもキューされますか？

デバイスが永続セッションで接続していれば、切断中のQoS1メッセージは保持され、同じクライアントIDで再接続したときに配られます。これは2025年の共有サブスクリプション向けキューとは別の、以前からある永続セッションの機能です。

### Amazon SQSとはどう使い分けますか？

ワーカーの再起動やデプロイによる停止が保持期限内に収まり、流入量・キュー件数・復帰後の処理能力も上限内なら、共有グループのキューを利用できます。1時間を超える停止があり得るデータや、失敗時の再試行・デッドレターキューが要るデータは、AWS IoTルールのSQSアクションでSQSへ送り、最大14日保持する構成にします。

## 関連記事

- [AWS IoT Coreとは？料金の東京単価と月額試算・MQTT接続・はじめ方](/tech/details/2991/)
- [MQTT QoSとは？0・1・2の違いと選び方をmosquitto・Pythonの実測で解説](/tech/details/3611/)
- [Python MQTT実装｜paho-mqtt 2系のループ方式と再接続を実装目線で解説](/tech/details/16866/)
- [ラズパイとAWS IoT Coreを接続する手順｜証明書作成・MQTT5送受信・常駐化](/tech/details/2803/)
- [Amazon SQSとは？FIFOキューと標準キューの違い・重複排除・可視性タイムアウトとDLQの設計【2026年10月】](/tech/details/17241/)

---

出典: [AWS IoT Coreのメッセージキューイングとは？共有サブスクリプションでQoS1を保持する条件と上限](<https://www.issoh.co.jp/tech/details/8039/>)（株式会社一創）
