mqttxx 3.1.2__py3-none-any.whl → 3.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mqttxx
3
- Version: 3.1.2
3
+ Version: 3.2.1
4
4
  Summary: 基于 aiomqtt 的高级 MQTT 客户端和 RPC 框架
5
5
  Author: MQTTX Team
6
6
  License: MIT
@@ -12,13 +12,14 @@ Requires-Dist: loguru >=0.7.0
12
12
  Provides-Extra: dev
13
13
  Requires-Dist: pytest ; extra == 'dev'
14
14
  Requires-Dist: pytest-asyncio ; extra == 'dev'
15
+ Requires-Dist: pytest-rich ; extra == 'dev'
15
16
  Requires-Dist: ruff ; extra == 'dev'
16
17
  Requires-Dist: build ; extra == 'dev'
17
18
  Requires-Dist: twine ; extra == 'dev'
18
19
 
19
20
  # MQTTX
20
21
 
21
- [![PyPI version](https://img.shields.io/badge/version-3.0.0-blue.svg)](https://pypi.org/project/mqttxx/)
22
+ [![PyPI version](https://img.shields.io/badge/version-3.2.0-blue.svg)](https://pypi.org/project/mqttxx/)
22
23
  [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
23
24
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
24
25
 
@@ -34,7 +35,6 @@ Requires-Dist: twine ; extra == 'dev'
34
35
  - ✅ **Event Channel** - 高吞吐事件广播,支持通配符订阅
35
36
  - ✅ **TLS/SSL 支持** - 安全连接、双向认证
36
37
  - ✅ **完善异常系统** - 统一错误码、清晰的异常层次
37
- - ✅ **可插拔编解码器** - 支持 JSON/MessagePack/Protobuf 等自定义协议
38
38
  - ✅ **生产级可靠性** - 修复所有 P0 缺陷(任务泄漏、资源泄漏、并发竞态)
39
39
 
40
40
  ---
@@ -50,11 +50,12 @@ Requires-Dist: twine ; extra == 'dev'
50
50
  - [传统 RPC](#4-rpc-基础用法传统模式)
51
51
  - [RPC 权限控制](#5-rpc-权限控制)
52
52
  - [TLS/SSL 和认证](#6-tlsssl-和认证)
53
+ - [架构与实现](#架构与实现)
53
54
  - [API 速查](#api-速查)
54
55
  - [配置对象](#配置对象)
55
56
  - [异常系统](#异常系统)
56
57
  - [RPC 消息协议](#rpc-消息协议)
57
- - [版本变更](#v200-重大变更)
58
+ - [版本变更](#版本变更)
58
59
  - [开发](#开发)
59
60
  - [示例项目](#示例项目)
60
61
  - [常见问题](#常见问题)
@@ -72,47 +73,23 @@ MQTTX 遵循严格的分层架构,确保职责清晰、代码简洁、易于
72
73
  - 不导入 `json` 或 `protocol` 模块
73
74
  - 不理解消息内容
74
75
  - `publish()` 只接受 `bytes`
75
- - `subscribe_raw()` handler 接收 `bytes`
76
+ - `subscribe()` handler 接收 `bytes`
76
77
 
77
78
  ### 2. Protocol(协议层)
78
- - **职责**:定义消息格式和编解码
79
+ - **职责**:定义消息格式和 JSON 编解码
79
80
  - **核心**:
80
- - `encode()` 方法:object → bytes
81
- - `decode()` 方法:bytes → object
82
- - JSON 只在这里出现
81
+ - `encode()` 方法:object → bytes(使用 JSON)
82
+ - `decode()` 方法:bytes → object(使用 JSON)
83
+ - 所有 JSON 操作只在这一层
83
84
 
84
85
  ### 3. RPCManager/EventChannelManager(应用层)
85
86
  - **职责**:业务逻辑处理
86
87
  - **约束**:
87
- - 内部永远使用类型化对象(RPCRequest/RPCResponse)
88
+ - 内部永远使用类型化对象(RPCRequest/RPCResponse/EventMessage
88
89
  - 调用 `encode()`/`decode()` 处理编解码
89
90
  - 不直接使用 `json.dumps()`/`json.loads()`
90
91
 
91
- ### 可插拔编解码器
92
-
93
- 支持自定义编解码器(例如 MessagePack):
94
-
95
- ```python
96
- import msgpack
97
- from mqttxx.protocol import Codec
98
-
99
- class MessagePackCodec:
100
- @staticmethod
101
- def encode(obj) -> bytes:
102
- if hasattr(obj, 'to_dict'):
103
- data = obj.to_dict()
104
- else:
105
- data = obj
106
- return msgpack.packb(data)
107
-
108
- @staticmethod
109
- def decode(data: bytes) -> dict:
110
- return msgpack.unpackb(data, raw=False)
111
-
112
- # 使用
113
- from mqttxx import RPCManager
114
- rpc = RPCManager(client, codec=MessagePackCodec)
115
- ```
92
+ **设计理念**:固定使用 JSON 编解码,简化架构,降低复杂度。如需自定义协议,可在传输层直接使用 `MQTTClient.subscribe()` 处理 bytes。
116
93
 
117
94
  ---
118
95
 
@@ -163,10 +140,10 @@ asyncio.run(main())
163
140
 
164
141
  ### 2. 约定式 RPC(零配置)
165
142
 
166
- **推荐**:使用 `ConventionalRPCManager`,自动订阅 + 自动注入 reply_to。
143
+ **推荐**:使用 `RPCManager` 的 `my_topic` 参数,自动订阅 + 自动注入 reply_to。
167
144
 
168
145
  ```python
169
- from mqttxx import MQTTClient, MQTTConfig, ConventionalRPCManager
146
+ from mqttxx import MQTTClient, MQTTConfig, RPCManager
170
147
 
171
148
  # 边缘设备
172
149
  async def edge_device():
@@ -178,7 +155,7 @@ async def edge_device():
178
155
 
179
156
  async with MQTTClient(config) as client:
180
157
  # 自动订阅 edge/device_123
181
- rpc = ConventionalRPCManager(client, my_topic=f"edge/{client_id}")
158
+ rpc = RPCManager(client, my_topic=f"edge/{client_id}")
182
159
 
183
160
  @rpc.register("get_status")
184
161
  async def get_status(params):
@@ -200,7 +177,7 @@ async def cloud_service():
200
177
 
201
178
  async with MQTTClient(config) as client:
202
179
  # 自动订阅 cloud/config-service
203
- rpc = ConventionalRPCManager(client, my_topic=f"cloud/{client_id}")
180
+ rpc = RPCManager(client, my_topic=f"cloud/{client_id}")
204
181
 
205
182
  @rpc.register("get_device_config")
206
183
  async def get_device_config(params):
@@ -220,7 +197,7 @@ asyncio.run(edge_device()) # 或 asyncio.run(cloud_service())
220
197
 
221
198
  | 场景 | 传统 RPC | 约定式 RPC |
222
199
  |-----|---------|-----------|
223
- | 初始化 | `rpc = RPCManager(client)`<br>`client.subscribe("edge/123", rpc.handle_rpc_message)` | `rpc = ConventionalRPCManager(client, my_topic="edge/123")`<br>→ 自动订阅 |
200
+ | 初始化 | `rpc = RPCManager(client)`<br>`client.subscribe("edge/123", rpc.handle_rpc_message)` | `rpc = RPCManager(client, my_topic="edge/123")`<br>→ 自动订阅 |
224
201
  | 调用 | `await rpc.call(topic="cloud/svc", method="get", reply_to="edge/123")` | `await rpc.call("cloud/svc", "get")` |
225
202
  | 代码量 | 100% | **60%** ↓ |
226
203
 
@@ -293,7 +270,7 @@ asyncio.run(main())
293
270
 
294
271
  ```python
295
272
  # 同时使用 RPC 和 Event Channel
296
- rpc = ConventionalRPCManager(client, my_topic="device/device_001")
273
+ rpc = RPCManager(client, my_topic="device/device_001")
297
274
  events = EventChannelManager(client)
298
275
 
299
276
  # RPC: 获取配置(需要返回值)
@@ -400,6 +377,17 @@ async with MQTTClient(config) as client:
400
377
 
401
378
  ---
402
379
 
380
+ ## 架构与实现
381
+
382
+ 深入了解 MQTTX 的技术实现细节:
383
+
384
+ - **[消息路由实现逻辑](docs/routing_flow.md)** - 使用 Mermaid 图表说明从 MQTT Broker 到最终处理器的完整消息流程、订阅注册机制、通配符匹配算法,以及关键代码位置
385
+ - **[约定式 RPC 用法详解](docs/约定式RPC用法.md)** - 深入解析零配置 RPC 调用的设计原理、自动订阅机制、reply_to 自动注入,以及与传统 RPC 的对比
386
+
387
+ **推荐阅读顺序**:先通过上方的"快速开始"掌握基本用法,再深入技术文档了解底层实现。
388
+
389
+ ---
390
+
403
391
  ## API 速查
404
392
 
405
393
  ### MQTTClient
@@ -422,7 +410,13 @@ class MQTTClient:
422
410
 
423
411
  ```python
424
412
  class RPCManager:
425
- def __init__(self, client: MQTTClient, config: RPCConfig = None, auth_callback: AuthCallback = None)
413
+ def __init__(
414
+ self,
415
+ client: MQTTClient,
416
+ my_topic: Optional[str] = None,
417
+ config: RPCConfig = None,
418
+ auth_callback: AuthCallback = None
419
+ )
426
420
 
427
421
  def register(self, method_name: str) # 装饰器
428
422
  def unregister(self, method_name: str) -> None
@@ -440,14 +434,14 @@ class RPCManager:
440
434
 
441
435
  ---
442
436
 
443
- ### ConventionalRPCManager(约定式 RPC)
437
+ ### RPCManager(约定式用法)
444
438
 
445
439
  ```python
446
- class ConventionalRPCManager(RPCManager):
440
+ class RPCManager:
447
441
  def __init__(
448
442
  self,
449
443
  client: MQTTClient,
450
- my_topic: str, # 本节点 topic(自动订阅,自动注入到 reply_to)
444
+ my_topic: Optional[str] = None, # 本节点 topic(自动订阅,自动注入到 reply_to)
451
445
  config: RPCConfig = None,
452
446
  auth_callback: AuthCallback = None,
453
447
  )
@@ -458,26 +452,26 @@ class ConventionalRPCManager(RPCManager):
458
452
  method: str,
459
453
  params: Any = None,
460
454
  timeout: float = None,
461
- reply_to: str = None, # 可选,默认使用 my_topic
455
+ reply_to: Optional[str] = None, # 可选,默认使用 my_topic
462
456
  ) -> Any
463
457
 
464
458
  # 属性
465
- my_topic: str # 当前 topic(只读)
459
+ my_topic: Optional[str] # 当前 topic(只读)
466
460
  ```
467
461
 
468
462
  **使用示例:**
469
463
 
470
464
  ```python
471
465
  # 边缘设备
472
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
466
+ rpc = RPCManager(client, my_topic="edge/device_123")
473
467
  config = await rpc.call("cloud/config-service", "get_config")
474
468
 
475
469
  # 云端服务
476
- rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
470
+ rpc = RPCManager(client, my_topic="cloud/config-service")
477
471
  status = await rpc.call("edge/device_123", "execute_command")
478
472
 
479
473
  # 微服务
480
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
474
+ rpc = RPCManager(client, my_topic="auth-service")
481
475
  user = await rpc.call("user-service", "get_user", params={"id": 123})
482
476
  ```
483
477
 
@@ -704,7 +698,34 @@ except RPCRemoteError as e:
704
698
 
705
699
  ---
706
700
 
707
- ## v2.0.0 重大变更
701
+ ## 版本变更
702
+
703
+ ### v3.2.0(当前版本)
704
+
705
+ **架构简化**:
706
+ - ✅ **移除可插拔编解码器** - 固定使用 JSON 编解码,简化架构设计
707
+ - ✅ **整合约定式 RPC** - `my_topic` 参数集成到 `RPCManager`,移除独立的 `ConventionalRPCManager`
708
+ - ✅ **整合传输层 API** - `RawAPI` 功能整合到 `MQTTClient`,统一接口
709
+
710
+ **迁移指南(从 v3.1.x)**:
711
+ 1. **移除 Codec 相关代码** - 不再支持自定义编解码器(如 MessagePack),统一使用 JSON
712
+ ```python
713
+ # ❌ 旧代码(不再支持)
714
+ rpc = RPCManager(client, codec=MessagePackCodec)
715
+
716
+ # ✅ 新代码(固定 JSON)
717
+ rpc = RPCManager(client)
718
+ ```
719
+ 2. **使用约定式 RPC** - 推荐使用 `my_topic` 参数简化代码
720
+ ```python
721
+ # ✅ 推荐写法
722
+ rpc = RPCManager(client, my_topic="edge/device_123")
723
+ result = await rpc.call("cloud/server", "get_config") # 自动注入 reply_to
724
+ ```
725
+
726
+ ---
727
+
728
+ ### v2.0.0 重大变更
708
729
 
709
730
  从 v2.0.0 开始,完全重写为基于 aiomqtt(纯 async/await),**不兼容** v0.x.x(gmqtt)。
710
731
 
@@ -712,13 +733,13 @@ except RPCRemoteError as e:
712
733
  - ✅ aiomqtt 替代 gmqtt
713
734
  - ✅ 原生 dataclass 替代 python-box(性能提升 6 倍)
714
735
  - ✅ **修复所有 P0 缺陷**(详见下方)
715
- - ✅ 新增约定式 RPC(`ConventionalRPCManager`)
736
+ - ✅ 新增约定式 RPC(`RPCManager` 的 `my_topic` 参数)
716
737
  - ✅ 新增权限控制(`auth_callback`)
717
738
  - ✅ 新增 TLS/SSL 支持
718
739
 
719
740
  **P0 缺陷修复(v3.0)**:
720
741
  1. **任务泄漏 + 消息处理模型** - Queue + Worker 模式,可控并发
721
- 2. **RawAPI.unsubscribe 缺失** - 实现完整的取消订阅功能
742
+ 2. **取消订阅功能缺失** - 实现完整的取消订阅功能
722
743
  3. **重连竞态条件** - 快照模式避免并发修改
723
744
  4. **连接资源泄漏** - 显式关闭连接
724
745
 
@@ -0,0 +1,12 @@
1
+ mqttxx/__init__.py,sha256=_WoU5oZfMOJetRH1JkLuICp8PlqxpN3BWyXeZEjXeVo,1908
2
+ mqttxx/client.py,sha256=7umy-hJ-TUW9XD_rMdTh71q9Z7rlifnVPvHA80aIbcI,18794
3
+ mqttxx/config.py,sha256=A4AK54FL3eNyOCQ4E5IowIkC34zpT43d1bJzX0VOAHk,5986
4
+ mqttxx/events.py,sha256=bs7Khp6ygDK_aEOXftb74HO3SMqOY_Ln9Se_ro6ryiE,11747
5
+ mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
6
+ mqttxx/protocol.py,sha256=EwXJQ0Ic25oJx55coAfvntOxWh5FYYlBSMV93Cm6hHU,7445
7
+ mqttxx/rpc.py,sha256=KBu2006o9NukqbUk7ehvyIwgrdbO1CqY4rdo-OKr1KM,15831
8
+ mqttxx-3.2.1.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
9
+ mqttxx-3.2.1.dist-info/METADATA,sha256=wFasvCdIOFRVZLnYEM84gxbRLE9uWd4CYa0VEtOFMGE,25544
10
+ mqttxx-3.2.1.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
11
+ mqttxx-3.2.1.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
12
+ mqttxx-3.2.1.dist-info/RECORD,,
mqttxx/conventions.py DELETED
@@ -1,148 +0,0 @@
1
- # 约定式 RPC 管理器 - 去角色设计
2
-
3
- from typing import Any, Optional, Type
4
- from loguru import logger
5
-
6
- from .client import MQTTClient
7
- from .config import RPCConfig
8
- from .rpc import RPCManager, AuthCallback
9
- from .protocol import Codec, JSONCodec
10
-
11
-
12
- class ConventionalRPCManager(RPCManager):
13
- """约定式 RPC 管理器
14
-
15
- 设计原则:
16
- - 约定优于配置
17
- - 自动订阅本地 topic
18
- - 自动注入 reply_to
19
-
20
- 核心功能:
21
- 1. 初始化时自动订阅 `my_topic`
22
- 2. 调用时自动将 `my_topic` 注入到 `reply_to`
23
-
24
- 适用场景:
25
- - 边缘设备 ↔ 云端(edge/xxx ↔ cloud/xxx)
26
- - 微服务之间(auth-service ↔ user-service)
27
- - IoT 网关 ↔ 设备(gateway/001 ↔ device/123)
28
- - 任何需要简化 RPC 调用的场景
29
-
30
- 示例:
31
- # 边缘设备
32
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
33
-
34
- @rpc.register("get_status")
35
- async def get_status(params):
36
- return {"status": "online"}
37
-
38
- # 调用云端(自动注入 reply_to="edge/device_123")
39
- config = await rpc.call("cloud/config-service", "get_config")
40
-
41
- # 云端服务
42
- rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
43
-
44
- # 调用边缘设备(自动注入 reply_to="cloud/config-service")
45
- status = await rpc.call("edge/device_123", "execute_command")
46
- """
47
-
48
- def __init__(
49
- self,
50
- client: MQTTClient,
51
- my_topic: str,
52
- config: Optional[RPCConfig] = None,
53
- auth_callback: Optional[AuthCallback] = None,
54
- codec: Type[Codec] = JSONCodec, # 新增
55
- ):
56
- """初始化约定式 RPC 管理器
57
-
58
- Args:
59
- client: MQTTClient 实例
60
- my_topic: 本节点的 topic(自动订阅,自动注入到 reply_to)
61
- config: RPC 配置(可选)
62
- auth_callback: 权限检查回调(可选)
63
- codec: 编解码器(默认 JSONCodec)
64
-
65
- 自动行为:
66
- - 自动订阅 my_topic(接收请求和响应)
67
- - 自动绑定消息处理器
68
-
69
- 示例:
70
- # 边缘设备
71
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
72
-
73
- # 云端服务
74
- rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
75
-
76
- # 微服务
77
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
78
-
79
- # 多层级
80
- rpc = ConventionalRPCManager(client, my_topic="region/zone/device")
81
- """
82
- super().__init__(client, config, auth_callback, codec)
83
-
84
- self._my_topic = my_topic
85
-
86
- # 自动设置(订阅响应主题)
87
- self.setup(my_topic)
88
-
89
- logger.info(f"ConventionalRPCManager 已初始化 - my_topic: {my_topic}")
90
-
91
- async def call(
92
- self,
93
- topic: str,
94
- method: str,
95
- params: Any = None,
96
- timeout: Optional[float] = None,
97
- reply_to: Optional[str] = None,
98
- ) -> Any:
99
- """调用远程方法(自动注入 reply_to)
100
-
101
- Args:
102
- topic: 对方的 topic
103
- method: 方法名
104
- params: 参数(可选)
105
- timeout: 超时时间(可选)
106
- reply_to: 响应 topic(可选,默认使用 my_topic)
107
-
108
- Returns:
109
- 方法返回值
110
-
111
- Raises:
112
- MQTTXError: 客户端未连接
113
- RPCTimeoutError: 调用超时
114
- RPCRemoteError: 远程执行失败
115
-
116
- 示例:
117
- # 边缘设备调用云端
118
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
119
- result = await rpc.call("cloud/server_001", "get_config")
120
- # 等价于:await super().call("cloud/server_001", "get_config", reply_to="edge/device_123")
121
-
122
- # 云端调用边缘设备
123
- rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
124
- result = await rpc.call("edge/device_123", "execute_command", params={"cmd": "restart"})
125
-
126
- # 微服务调用
127
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
128
- user = await rpc.call("user-service", "get_user", params={"id": 123})
129
- """
130
- # 自动注入 reply_to
131
- reply_to = reply_to or self._my_topic
132
-
133
- return await super().call(
134
- topic=topic,
135
- method=method,
136
- params=params,
137
- timeout=timeout,
138
- reply_to=reply_to,
139
- )
140
-
141
- @property
142
- def my_topic(self) -> str:
143
- """获取当前节点的 topic
144
-
145
- Returns:
146
- 当前节点订阅的 topic
147
- """
148
- return self._my_topic
@@ -1,13 +0,0 @@
1
- mqttxx/__init__.py,sha256=bNvLfcG5AgCpOrS1B4LEsDeVi-cKLuw0fYTOdlJoxS8,2072
2
- mqttxx/client.py,sha256=lc1V6yPqq15PhmmU6L1Tspikg1b18jPLTkZe7ElmSTE,21807
3
- mqttxx/config.py,sha256=A4AK54FL3eNyOCQ4E5IowIkC34zpT43d1bJzX0VOAHk,5986
4
- mqttxx/conventions.py,sha256=ZP5FkYBCJiee1co041pbPv4x31i9HuG9KbeUFaDsoBs,4683
5
- mqttxx/events.py,sha256=IG6ca4Gmm1P4sJUmZXTw6I2lHjVawJkpBjzX1GNerBM,11368
6
- mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
7
- mqttxx/protocol.py,sha256=GZYL2Fvi4-R0Tuyxrcp8-ttLWx9wyoH4-ZtzNq1wYxo,10034
8
- mqttxx/rpc.py,sha256=6syEzmDZr2BQE2_LbdtrdTeWkrz3MAlAebbuX9O5-pY,15197
9
- mqttxx-3.1.2.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
10
- mqttxx-3.1.2.dist-info/METADATA,sha256=ncDgBLxFEkCYeQhIz1nO3Mt5SUrodJq89U2ONATVoOg,24337
11
- mqttxx-3.1.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
12
- mqttxx-3.1.2.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
13
- mqttxx-3.1.2.dist-info/RECORD,,
File without changes