Python MQTTクライアントの比較



人工知能とビッグデータの発展に伴い、Pythonはその優雅な言語スタイル、豊富なライブラリ、そして使いやすさから、最も人気のあるプログラミング言語の一つとなりました。また、IoTなどの様々な分野にも浸透し始めています。

Pythonには多くの優れたMQTTクライアントライブラリが存在します。これらのクライアントを使用すると、非常に少ないコードでMQTTクライアントアプリケーションを開発できます。

この記事では、一般的なPython MQTTクライアントライブラリを3つ集め、ライブラリの開発や使用の複雑さなどの観点から簡単に比較し、読者が選択する際の参考になるようにします。同時に、この記事では簡単なPythonの例を提供し、例のコードをエディタにコピーして直接実行することができます（Python 3.5以上が必要で、対応する依存パッケージをインストールする必要があります）。

## paho-mqtt

[paho-mqtt](https://github.com/eclipse/paho.mqtt.python)は、PythonのMQTTオープンソースクライアントライブラリの中で最も優れていると言えます。Eclipse Foundationのリーダーシップの下で開発され、Pythonライブラリの他にも、C++、Java、JavaScript、Golangなどの主要なプログラミング言語をサポートしています。現在のPythonバージョンでは、3.1および3.1.1の[MQTTプロトコル](https://www.emqx.com/en/mqtt-guide)を実装し、最新の開発バージョンでは[MQTT 5.0](https://www.emqx.com/en/blog/introduction-to-mqtt-5)も実装しています。

資金のサポートを受けて、年に1回のペースで更新されています。この記事が公開された時点での最新バージョンは1.5.0（2019年8月リリース）でした。

GitHubのホームページでは、使い始めるためのクイック実装から各機能まで、詳細な説明が提供されており、初心者から上級者まで必要なことはすべて網羅されています。範囲外の問題が発生した場合でも、Googleで検索すれば、約20万件の関連エントリーが得られます。最も人気のあるMQTTクライアントです。

コードの安定性と使いやすさがその理由です。Pahoのインターフェースは非常にシンプルで使いやすく、MQTTのサブスクリプションとメッセージングを実装するために少量のコードだけで済みます。

### インストール

```shell
pip3 install paho-mqtt
```

または

```shell
git clone https://github.com/eclipse/paho.mqtt.python
cd paho.mqtt.python
python3 setup.py install
```

### サブスクライバー

```python
import paho.mqtt.client as mqtt

# 接続のコールバック関数
def on_connect(client, userdata, flags, rc):
    print(f"Connected with result code {rc}")
    client.subscribe("$SYS/#")

# 受信メッセージのコールバック関数
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.emqx.io", 1883, 60)
client.loop_forever()
```

### パブリッシャー

```python
import paho.mqtt.client as mqtt
import time

def on_connect(client, userdata, flags, rc):
    print(f"Connected with result code {rc}")

client = mqtt.Client()
client.on_connect = on_connect
client.connect("broker.emqx.io", 1883, 60)

for i in range(3):
    client.publish('a/b', payload=i, qos=0, retain=False)
    print(f"send {i} to a/b")
    time.sleep(1)

client.loop_forever()
```

たった一行のコードでもサブスクリプションとパブリッシュを実装できます。

```python
import paho.mqtt.subscribe as subscribe

# この関数が呼ばれると、トピックpaho/test/simpleにメッセージが送信されるまでプログラムはここでブロックされる
msg = subscribe.simple("paho/test/simple", hostname="broker.emqx.io")
print(f"{msg.topic} {msg.payload}")

import paho.mqtt.publish as publish

# メッセージを送信する
publish.single("a/b", "payload", hostname="broker.emqx.io")
# または一度に複数のメッセージを送信する
msgs = [{'topic':"a/b", 'payload':"multiple 1"}, ("a/b", "multiple 2", 0, False)]
publish.multiple(msgs, hostname="broker.emqx.io")
```

## HBMQTT

[HBMQTT](https://github.com/beerfactory/hbmqtt)はPython asyncioをベースに開発され、3.1.1 MQTTプロトコルのみをサポートしています。asyncioライブラリを使用しているため、Python 3.4以上が必要です。

CPUはディスク、ネットワーク、その他のIO操作よりもはるかに高速です。しかし、スレッド内では、どれだけCPUが高速に実行されていても、IO操作に遭遇すると、読み書きが完了するまで停止して待つ必要があり、多くの時間が無駄になります。

この問題を解決するために、Pythonには非同期IOの機能が追加されました。Python 3.4でasyncioが標準ライブラリに公式に追加され、Python 3.5ではキーワードasync/awaitが追加されました。ユーザーは関数の前にasyncキーワードを簡単に使用して、それらを非同期関数にすることができます。

HBMQTTはasyncio標準ライブラリの上に構築されています。ユーザーは非同期ブレークポイントを明示的に設定することができます。非同期IOを通じて、MQTTクライアントはメッセージの受信または送信時に現在のタスクを保留し、次のタスクを続けることができます。

ただし、HBMQTTの知名度はそれほど高くありません。GoogleでHBMQTTを検索すると、わずか6,000件以上のエントリーが得られ、Stack Overflowではたった10の質問があります。つまり、HBMQTTを使用する場合は、問題解決能力が強くなければなりません。

興味深いことに、HBMQTTは[MQTTブローカー](https://www.emqx.com/en/products/emqx)でもあります。hbmqttコマンドを通じてワンクリックで有効にすることができます。

```
$ hbmqtt
[2020-08-28 09:35:56,608] :: INFO - Exited state new
[2020-08-28 0935:56,608] :: INFO - Entered state starting
[2020-08-28 09:35:56,609] :: INFO - Listener 'default' bind to 0.0.0.0:1883 (max_connections=-1)
```

### インストール

```
pip3 install hbmqtt
```

または

```
git clone https://github.com/beerfactory/hbmqtt
cd hbmqtt
python3 setup.py install
```

### サブスクライバー

### パブリッシャー

公式ドキュメントはこちらを参照してください：https://hbmqtt.readthedocs.io/en/latest/。

## gmqtt

[gmqtt](https://github.com/wialon/gmqtt)は個人開発者によってオープンソース化されたクライアントライブラリです。デフォルトでMQTT 5.0プロトコルをサポートしていますが、接続されたMQTTエージェントが5.0プロトコルをサポートしていない場合は、3.1にダウングレードされ、再接続されます。

最初の2つと比較して、gmqttはまだ開発の初期段階にあり、この記事が公開された時点でのバージョンは0.6.7です。しかし、MQTT 5.0を早期にサポートしたPythonライブラリの一つであるため、ウェブ上ではよく知られています。

これもasyncioライブラリをベースに構築されているため、Python 3.4以降が必要です。

### インストール

```
pip3 install gmqtt
```

または

```
git clone https://github.com/wialon/gmqtt
cd gmqtt
python3 setup.py install
```

### サブスクライバー

```py
import asyncio
import os
import signal
import time
from gmqtt import Client as MQTTClient

STOP = asyncio.Event()

def on_connect(client, flags, rc, properties):
    print('Connected')

def on_message(client, topic, payload, qos, properties):
    print(f'RECV MSG: {topic} {payload}')

def on_subscribe(client, mid, qos, properties):
    print('SUBSCRIBED')

def on_disconnect(client, packet, exc=None):
    print('Disconnected')

def ask_exit(*args):
    STOP.set()

async def main(broker_host):
    client = MQTTClient("client-id")

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_subscribe = on_subscribe
    client.on_disconnect = on_disconnect

    # Connectting the MQTT broker
    await client.connect(broker_host)

    # Subscribe to topic
    client.subscribe('TEST/#')

    # Send the data of test
    client.publish("TEST/A", 'AAA')
    client.publish("TEST/B", 'BBB')

    await STOP.wait()
    await client.disconnect()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()

    loop.add_signal_handler(signal.SIGINT, ask_exit)
    loop.add_signal_handler(signal.SIGTERM, ask_exit)

    host = 'broker.emqx.io'
    loop.run_until_complete(main(host))
```

### パブリッシャー

```python
import asyncio
import os
import signal
import time
from gmqtt import Client as MQTTClient

STOP = asyncio.Event()

def on_connect(client, flags, rc, properties):
    print('Connected')
    client.subscribe('TEST/#', qos=0)

def on_message(client, topic, payload, qos, properties):
    print(f'RECV MSG: {topic}, {payload}')

def on_disconnect(client, packet, exc=None):
    print('Disconnected')

def ask_exit(*args):
    STOP.set()

async def main(broker_host):
    client = MQTTClient("client-id")

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect

    await client.connect(broker_host)

    client.publish('TEST/TIME', str(time.time()), qos=1)

    await STOP.wait()
    await client.disconnect()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()

    loop.add_signal_handler(signal.SIGINT, ask_exit)
    loop.add_signal_handler(signal.SIGTERM, ask_exit)

    host = 'broker.emqx.io'  
    loop.run_until_complete(main(host))
```

## Python MQTTクライアントはどう選択するか

これら3つのPython MQTTクライアントライブラリを紹介した後、要件に合うMQTTクライアントライブラリの選択方法を見てみましょう。3つのクライアントはそれぞれ長所と短所があります：

paho-mqttは最も良いドキュメントを持ち、理解しやすいコードスタイル、強力な財団のサポートを持っていますが、現在のバージョンのドキュメントはMQTT 5.0をサポートしていません。

HBMQTTの実装はasyncioライブラリを使用しており、ネットワークI/Oによって引き起こされる遅延を最適化することができます。しかし、コードスタイルはユーザーフレンドリーではなく、MQTT 5.0もサポートしていません。

gmqttもasyncioライブラリをベースに実装されています。HBMQTTと比較して、よりフレンドリーなコードスタイルを持っています。最も重要なことは、MQTT 5.0をサポートしていますが、開発プロセスが遅く、将来は不確実です。

したがって、以下の考え方を参考に選択してください：

- 通常の開発者で、本番環境で使用したい場合、paho-mqttは間違いなく最良の選択です。その安定性とコードの読みやすさは、他の2つのライブラリをはるかに上回っています。問題が発生した場合、優れたドキュメントとインターネット上の多数のエントリーがより多くの解決策を見つけるのに役立ちます。
- asyncioライブラリに精通している読者は、HBMQTTとgmqttを試してみることができます。
- MQTT 5.0を学びたい、またはオープンソースプロジェクトに参加したい場合、gmqttを試してみて、それに対するプルリクエストを送信してみてください。

以上が、PythonのMQTTクライアントライブラリを選択する際の一つの考え方です。あなたのニーズに合ったものを選択し、PythonでのMQTTの実装に役立ててください。