sycommon-python-lib 0.1.41__tar.gz → 0.1.45__tar.gz
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.
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/PKG-INFO +7 -7
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/pyproject.toml +11 -7
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/traceid.py +1 -1
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/rabbitmq/rabbitmq_client.py +133 -29
- sycommon_python_lib-0.1.45/src/sycommon/rabbitmq/rabbitmq_pool.py +404 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/rabbitmq/rabbitmq_service.py +195 -83
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/services.py +2 -1
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/feign.py +9 -8
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/nacos_service.py +73 -52
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/PKG-INFO +7 -7
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/requires.txt +6 -6
- sycommon_python_lib-0.1.41/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -312
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/README.md +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/setup.cfg +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.45
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: aio-pika>=9.5.
|
|
8
|
-
Requires-Dist: aiohttp>=3.13.
|
|
7
|
+
Requires-Dist: aio-pika>=9.5.8
|
|
8
|
+
Requires-Dist: aiohttp>=3.13.2
|
|
9
9
|
Requires-Dist: decorator>=5.2.1
|
|
10
|
-
Requires-Dist: fastapi>=0.
|
|
11
|
-
Requires-Dist: kafka-python>=2.2.
|
|
10
|
+
Requires-Dist: fastapi>=0.121.2
|
|
11
|
+
Requires-Dist: kafka-python>=2.2.16
|
|
12
12
|
Requires-Dist: loguru>=0.7.3
|
|
13
13
|
Requires-Dist: mysql-connector-python>=9.5.0
|
|
14
14
|
Requires-Dist: nacos-sdk-python>=2.0.9
|
|
15
|
-
Requires-Dist: pydantic>=2.12.
|
|
15
|
+
Requires-Dist: pydantic>=2.12.4
|
|
16
16
|
Requires-Dist: python-dotenv>=1.2.1
|
|
17
17
|
Requires-Dist: pyyaml>=6.0.3
|
|
18
18
|
Requires-Dist: sqlalchemy>=2.0.44
|
|
19
|
-
Requires-Dist: starlette>=0.
|
|
19
|
+
Requires-Dist: starlette>=0.49.3
|
|
20
20
|
Requires-Dist: uuid>=1.30
|
|
21
21
|
Requires-Dist: uvicorn>=0.38.0
|
|
22
22
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sycommon-python-lib"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.45"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"aio-pika>=9.5.
|
|
9
|
-
"aiohttp>=3.13.
|
|
8
|
+
"aio-pika>=9.5.8",
|
|
9
|
+
"aiohttp>=3.13.2",
|
|
10
10
|
"decorator>=5.2.1",
|
|
11
|
-
"fastapi>=0.
|
|
12
|
-
"kafka-python>=2.2.
|
|
11
|
+
"fastapi>=0.121.2",
|
|
12
|
+
"kafka-python>=2.2.16",
|
|
13
13
|
"loguru>=0.7.3",
|
|
14
14
|
"mysql-connector-python>=9.5.0",
|
|
15
15
|
"nacos-sdk-python>=2.0.9",
|
|
16
|
-
"pydantic>=2.12.
|
|
16
|
+
"pydantic>=2.12.4",
|
|
17
17
|
"python-dotenv>=1.2.1",
|
|
18
18
|
"pyyaml>=6.0.3",
|
|
19
19
|
"sqlalchemy>=2.0.44",
|
|
20
|
-
"starlette>=0.
|
|
20
|
+
"starlette>=0.49.3",
|
|
21
21
|
"uuid>=1.30",
|
|
22
22
|
"uvicorn>=0.38.0",
|
|
23
23
|
]
|
|
@@ -25,5 +25,9 @@ dependencies = [
|
|
|
25
25
|
[tool.setuptools]
|
|
26
26
|
packages = {find = {where = ["src"]}}
|
|
27
27
|
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["setuptools"]
|
|
30
|
+
build-backend = "setuptools.build_meta"
|
|
31
|
+
|
|
28
32
|
[project.scripts]
|
|
29
33
|
sycommon = "command.cli:main"
|
{sycommon_python_lib-0.1.41 → sycommon_python_lib-0.1.45}/src/sycommon/rabbitmq/rabbitmq_client.py
RENAMED
|
@@ -9,16 +9,13 @@ from aio_pika.abc import (
|
|
|
9
9
|
AbstractQueue,
|
|
10
10
|
AbstractIncomingMessage,
|
|
11
11
|
ConsumerTag,
|
|
12
|
-
AbstractRobustConnection
|
|
12
|
+
AbstractRobustConnection,
|
|
13
13
|
)
|
|
14
14
|
from sycommon.rabbitmq.rabbitmq_pool import RabbitMQConnectionPool
|
|
15
15
|
from sycommon.logging.kafka_log import SYLogger
|
|
16
16
|
from sycommon.models.mqmsg_model import MQMsgModel
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
# 最大重试次数限制
|
|
20
|
-
MAX_RETRY_COUNT = 3
|
|
21
|
-
|
|
22
19
|
logger = SYLogger
|
|
23
20
|
|
|
24
21
|
|
|
@@ -27,10 +24,10 @@ class RabbitMQClient:
|
|
|
27
24
|
RabbitMQ 客户端(支持消息发布、消费、自动重连、异常重试)
|
|
28
25
|
核心特性:
|
|
29
26
|
1. 基于连接池复用资源,性能优化
|
|
30
|
-
2.
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
27
|
+
2. 连接/通道失效时自动重建,高可用(限制并发重连)
|
|
28
|
+
3. 消息发布支持重试+mandatory机制+超时控制,确保路由有效
|
|
29
|
+
4. 消费支持手动ACK/NACK
|
|
30
|
+
5. 兼容JSON/字符串/字典消息格式
|
|
34
31
|
"""
|
|
35
32
|
|
|
36
33
|
def __init__(
|
|
@@ -45,7 +42,6 @@ class RabbitMQClient:
|
|
|
45
42
|
auto_parse_json: bool = True,
|
|
46
43
|
create_if_not_exists: bool = True,
|
|
47
44
|
prefetch_count: int = 2,
|
|
48
|
-
# 兼容旧代码参数(无需使用)
|
|
49
45
|
**kwargs,
|
|
50
46
|
):
|
|
51
47
|
# 依赖注入:连接池(必须已初始化)
|
|
@@ -85,6 +81,20 @@ class RabbitMQClient:
|
|
|
85
81
|
# 线程安全锁
|
|
86
82
|
self._consume_lock = asyncio.Lock()
|
|
87
83
|
self._connect_lock = asyncio.Lock()
|
|
84
|
+
# 跟踪连接关闭回调(用于后续移除)
|
|
85
|
+
self._conn_close_callback: Optional[Callable] = None
|
|
86
|
+
# 控制重连频率的信号量(限制并发重连数,默认1个)
|
|
87
|
+
self._reconnect_semaphore = asyncio.Semaphore(1)
|
|
88
|
+
# 固定重连间隔15秒(全局统一)
|
|
89
|
+
self._RECONNECT_INTERVAL = 15
|
|
90
|
+
# 重连任务锁(确保同一时间只有一个重连任务)
|
|
91
|
+
self._reconnect_task_lock = asyncio.Lock()
|
|
92
|
+
# 跟踪当前重连任务(避免重复创建)
|
|
93
|
+
self._current_reconnect_task: Optional[asyncio.Task] = None
|
|
94
|
+
# 连接失败计数器(用于告警)
|
|
95
|
+
self._reconnect_fail_count = 0
|
|
96
|
+
# 连接失败告警阈值
|
|
97
|
+
self._reconnect_alert_threshold = 5
|
|
88
98
|
|
|
89
99
|
@property
|
|
90
100
|
async def is_connected(self) -> bool:
|
|
@@ -104,14 +114,16 @@ class RabbitMQClient:
|
|
|
104
114
|
return False
|
|
105
115
|
|
|
106
116
|
async def connect(self) -> None:
|
|
107
|
-
"""建立连接并初始化交换机/队列(支持重连)"""
|
|
108
117
|
if self._closed:
|
|
109
118
|
raise RuntimeError("客户端已关闭,无法重新连接")
|
|
110
119
|
|
|
111
120
|
async with self._connect_lock:
|
|
112
|
-
#
|
|
121
|
+
# 释放旧资源(保留原有回调清理逻辑)
|
|
113
122
|
if self._channel and self._channel_conn:
|
|
114
123
|
try:
|
|
124
|
+
if self._conn_close_callback and self._channel_conn:
|
|
125
|
+
self._channel_conn.close_callbacks.discard(
|
|
126
|
+
self._conn_close_callback)
|
|
115
127
|
await self.connection_pool.release_channel(self._channel, self._channel_conn)
|
|
116
128
|
except Exception as e:
|
|
117
129
|
SYLogger.warning(f"释放旧通道失败: {str(e)}")
|
|
@@ -119,16 +131,38 @@ class RabbitMQClient:
|
|
|
119
131
|
self._channel_conn = None
|
|
120
132
|
self._exchange = None
|
|
121
133
|
self._queue = None
|
|
134
|
+
self._conn_close_callback = None
|
|
122
135
|
|
|
123
136
|
try:
|
|
124
|
-
#
|
|
137
|
+
# 从连接池获取通道+连接(连接池返回的是 RobustChannel)
|
|
125
138
|
self._channel, self._channel_conn = await self.connection_pool.acquire_channel()
|
|
126
139
|
|
|
140
|
+
def on_conn_closed(conn: AbstractRobustConnection, exc: Optional[BaseException]):
|
|
141
|
+
"""连接关闭回调:触发固定间隔重连"""
|
|
142
|
+
SYLogger.warning(
|
|
143
|
+
f"客户端连接关闭: {conn!r},原因: {exc}", exc_info=exc)
|
|
144
|
+
self._reconnect_fail_count += 1
|
|
145
|
+
# 超过阈值告警
|
|
146
|
+
if self._reconnect_fail_count >= self._reconnect_alert_threshold:
|
|
147
|
+
SYLogger.error(
|
|
148
|
+
f"连接失败次数已达阈值({self._reconnect_alert_threshold}),请检查MQ服务状态")
|
|
149
|
+
if not self._closed:
|
|
150
|
+
asyncio.create_task(self._safe_reconnect())
|
|
151
|
+
|
|
152
|
+
self._conn_close_callback = on_conn_closed
|
|
153
|
+
if self._channel_conn:
|
|
154
|
+
self._channel_conn.close_callbacks.add(
|
|
155
|
+
self._conn_close_callback)
|
|
156
|
+
|
|
127
157
|
# 2. 设置预取计数(限流)
|
|
128
158
|
await self._channel.set_qos(prefetch_count=self.prefetch_count)
|
|
129
159
|
SYLogger.debug(f"设置预取计数: {self.prefetch_count}")
|
|
130
160
|
|
|
131
|
-
# 3.
|
|
161
|
+
# 3. 低版本 RobustChannel 说明:默认启用异步发布确认,无显式确认方法
|
|
162
|
+
SYLogger.debug(
|
|
163
|
+
"基于 RobustChannel 异步发布确认(低版本 aio-pika 不支持显式确认方法)")
|
|
164
|
+
|
|
165
|
+
# 4. 声明交换机
|
|
132
166
|
self._exchange = await self._channel.declare_exchange(
|
|
133
167
|
name=self.exchange_name,
|
|
134
168
|
type=self.exchange_type,
|
|
@@ -139,7 +173,7 @@ class RabbitMQClient:
|
|
|
139
173
|
SYLogger.info(
|
|
140
174
|
f"交换机初始化成功: {self.exchange_name}(类型: {self.exchange_type.value})")
|
|
141
175
|
|
|
142
|
-
#
|
|
176
|
+
# 5. 声明队列(如果配置了队列名)
|
|
143
177
|
if self.queue_name:
|
|
144
178
|
self._queue = await self._channel.declare_queue(
|
|
145
179
|
name=self.queue_name,
|
|
@@ -157,10 +191,15 @@ class RabbitMQClient:
|
|
|
157
191
|
f"(绑定交换机: {self.exchange_name}, routing_key: {self.routing_key})"
|
|
158
192
|
)
|
|
159
193
|
|
|
194
|
+
# 重连成功,重置失败计数器
|
|
195
|
+
self._reconnect_fail_count = 0
|
|
160
196
|
SYLogger.info("客户端连接初始化完成")
|
|
161
197
|
except Exception as e:
|
|
162
198
|
SYLogger.error(f"客户端连接失败: {str(e)}", exc_info=True)
|
|
163
199
|
# 清理异常状态
|
|
200
|
+
if self._conn_close_callback and self._channel_conn:
|
|
201
|
+
self._channel_conn.close_callbacks.discard(
|
|
202
|
+
self._conn_close_callback)
|
|
164
203
|
if self._channel and self._channel_conn:
|
|
165
204
|
try:
|
|
166
205
|
await self.connection_pool.release_channel(self._channel, self._channel_conn)
|
|
@@ -168,8 +207,45 @@ class RabbitMQClient:
|
|
|
168
207
|
pass
|
|
169
208
|
self._channel = None
|
|
170
209
|
self._channel_conn = None
|
|
210
|
+
# 触发重连(固定间隔)
|
|
211
|
+
if not self._closed:
|
|
212
|
+
asyncio.create_task(self._safe_reconnect())
|
|
171
213
|
raise
|
|
172
214
|
|
|
215
|
+
async def _safe_reconnect(self):
|
|
216
|
+
"""安全重连:信号量控制并发+固定15秒间隔,避免短时间大量重连"""
|
|
217
|
+
# 1. 信号量控制:限制同时进行的重连任务数(默认1个)
|
|
218
|
+
async with self._reconnect_semaphore:
|
|
219
|
+
# 2. 检查是否已有重连任务在运行(双重保障)
|
|
220
|
+
if self._current_reconnect_task and not self._current_reconnect_task.done():
|
|
221
|
+
SYLogger.debug("已有重连任务在运行,跳过重复触发")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
async with self._reconnect_task_lock:
|
|
225
|
+
if self._closed or await self.is_connected:
|
|
226
|
+
SYLogger.debug("客户端已关闭或已连接,取消重连")
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
# 3. 固定15秒重连间隔(避免频繁重试)
|
|
230
|
+
SYLogger.info(f"将在15秒后尝试重连...")
|
|
231
|
+
await asyncio.sleep(self._RECONNECT_INTERVAL)
|
|
232
|
+
|
|
233
|
+
if self._closed or await self.is_connected:
|
|
234
|
+
SYLogger.debug("重连等待期间客户端状态变化,取消重连")
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
# 4. 执行重连
|
|
239
|
+
SYLogger.info("开始重连RabbitMQ客户端...")
|
|
240
|
+
self._current_reconnect_task = asyncio.create_task(
|
|
241
|
+
self.connect())
|
|
242
|
+
await self._current_reconnect_task
|
|
243
|
+
except Exception as e:
|
|
244
|
+
SYLogger.warning(f"重连失败: {str(e)}")
|
|
245
|
+
# 重连失败后,不主动触发下一次(等待连接关闭回调再次触发,避免死循环)
|
|
246
|
+
finally:
|
|
247
|
+
self._current_reconnect_task = None
|
|
248
|
+
|
|
173
249
|
async def set_message_handler(
|
|
174
250
|
self,
|
|
175
251
|
handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]],
|
|
@@ -242,7 +318,7 @@ class RabbitMQClient:
|
|
|
242
318
|
# 检查连接状态,失效则触发重连
|
|
243
319
|
if not await self.is_connected:
|
|
244
320
|
SYLogger.warning("连接已失效,触发客户端重连")
|
|
245
|
-
asyncio.create_task(self.
|
|
321
|
+
asyncio.create_task(self._safe_reconnect())
|
|
246
322
|
|
|
247
323
|
# 3. 启动消费
|
|
248
324
|
self._consumer_tag = await self._queue.consume(consume_callback)
|
|
@@ -272,7 +348,7 @@ class RabbitMQClient:
|
|
|
272
348
|
retry_count: int = 3,
|
|
273
349
|
) -> None:
|
|
274
350
|
"""
|
|
275
|
-
发布消息(支持自动重试、
|
|
351
|
+
发布消息(支持自动重试、mandatory路由校验、5秒超时控制)
|
|
276
352
|
:param message_body: 消息体(字符串/字典/MQMsgModel)
|
|
277
353
|
:param headers: 消息头(可选)
|
|
278
354
|
:param content_type: 内容类型(默认application/json)
|
|
@@ -314,40 +390,64 @@ class RabbitMQClient:
|
|
|
314
390
|
SYLogger.warning(f"发布消息前连接失效,触发重连(retry: {retry})")
|
|
315
391
|
await self.connect()
|
|
316
392
|
|
|
317
|
-
#
|
|
318
|
-
await self._exchange.publish(
|
|
393
|
+
# 核心:发布消息(添加 mandatory=True 和 timeout=5.0)
|
|
394
|
+
publish_result = await self._exchange.publish(
|
|
319
395
|
message=message,
|
|
320
396
|
routing_key=self.routing_key or self.queue_name or "#",
|
|
397
|
+
mandatory=True, # 强制路由到至少一个队列,否则返回None
|
|
398
|
+
timeout=5.0 # 5秒超时控制,避免无限阻塞
|
|
321
399
|
)
|
|
400
|
+
|
|
401
|
+
# 处理 mandatory=True 结果:未路由到队列返回 None,直接抛出异常
|
|
402
|
+
if publish_result is None:
|
|
403
|
+
raise RuntimeError(
|
|
404
|
+
f"消息未找到匹配的队列(routing_key: {self.routing_key}),mandatory=True 触发失败"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# 低版本 RobustChannel 异步确认,无需显式等待,仅日志说明
|
|
322
408
|
SYLogger.info(
|
|
323
409
|
f"消息发布成功(retry: {retry}),routing_key: {self.routing_key},"
|
|
324
|
-
f"delivery_mode: {delivery_mode.value}"
|
|
410
|
+
f"delivery_mode: {delivery_mode.value},mandatory: True,timeout: 5.0s"
|
|
325
411
|
)
|
|
326
412
|
return
|
|
413
|
+
except asyncio.TimeoutError:
|
|
414
|
+
SYLogger.error(
|
|
415
|
+
f"消息发布超时(retry: {retry}/{retry_count-1}),超时时间: 5.0s"
|
|
416
|
+
)
|
|
417
|
+
except RuntimeError as e:
|
|
418
|
+
# 捕获 mandatory 未路由等业务异常
|
|
419
|
+
SYLogger.error(
|
|
420
|
+
f"消息发布业务失败(retry: {retry}/{retry_count-1}): {str(e)}"
|
|
421
|
+
)
|
|
327
422
|
except Exception as e:
|
|
328
423
|
SYLogger.error(
|
|
329
424
|
f"消息发布失败(retry: {retry}/{retry_count-1}): {str(e)}",
|
|
330
425
|
exc_info=True
|
|
331
426
|
)
|
|
332
|
-
#
|
|
427
|
+
# 清理失效状态,下次重试重连
|
|
333
428
|
self._exchange = None
|
|
334
|
-
|
|
335
|
-
|
|
429
|
+
# 指数退避重试间隔(0.5s, 1s, 2s...)
|
|
430
|
+
await asyncio.sleep(0.5 * (2 ** retry))
|
|
336
431
|
|
|
337
|
-
#
|
|
432
|
+
# 所有重试失败,抛出最终异常
|
|
338
433
|
raise RuntimeError(
|
|
339
|
-
f"消息发布失败(已重试{retry_count}次),routing_key: {self.routing_key}"
|
|
434
|
+
f"消息发布失败(已重试{retry_count}次),routing_key: {self.routing_key},"
|
|
435
|
+
f"mandatory: True,timeout: 5.0s"
|
|
340
436
|
)
|
|
341
437
|
|
|
342
438
|
async def close(self) -> None:
|
|
343
|
-
"""
|
|
344
|
-
if self._closed:
|
|
345
|
-
SYLogger.warning("客户端已关闭,无需重复操作")
|
|
346
|
-
return
|
|
347
|
-
|
|
439
|
+
"""关闭客户端(移除回调+释放资源)"""
|
|
348
440
|
self._closed = True
|
|
349
441
|
SYLogger.info("开始关闭RabbitMQ客户端...")
|
|
350
442
|
|
|
443
|
+
# 停止重连任务
|
|
444
|
+
if self._current_reconnect_task and not self._current_reconnect_task.done():
|
|
445
|
+
self._current_reconnect_task.cancel()
|
|
446
|
+
try:
|
|
447
|
+
await self._current_reconnect_task
|
|
448
|
+
except asyncio.CancelledError:
|
|
449
|
+
SYLogger.debug("重连任务已取消")
|
|
450
|
+
|
|
351
451
|
# 1. 停止消费
|
|
352
452
|
await self.stop_consuming()
|
|
353
453
|
|
|
@@ -355,6 +455,10 @@ class RabbitMQClient:
|
|
|
355
455
|
async with self._connect_lock:
|
|
356
456
|
if self._channel and self._channel_conn:
|
|
357
457
|
try:
|
|
458
|
+
# 移除连接关闭回调
|
|
459
|
+
if self._conn_close_callback:
|
|
460
|
+
self._channel_conn.close_callbacks.discard(
|
|
461
|
+
self._conn_close_callback)
|
|
358
462
|
await self.connection_pool.release_channel(self._channel, self._channel_conn)
|
|
359
463
|
SYLogger.info("通道释放成功")
|
|
360
464
|
except Exception as e:
|