sycommon-python-lib 0.1.21__py3-none-any.whl → 0.1.46__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.
- sycommon/database/database_service.py +6 -1
- sycommon/logging/kafka_log.py +18 -8
- sycommon/logging/sql_logger.py +53 -0
- sycommon/middleware/middleware.py +1 -1
- sycommon/middleware/traceid.py +1 -1
- sycommon/models/mqlistener_config.py +1 -0
- sycommon/rabbitmq/rabbitmq_client.py +393 -600
- sycommon/rabbitmq/rabbitmq_pool.py +548 -66
- sycommon/rabbitmq/rabbitmq_service.py +381 -174
- sycommon/services.py +65 -22
- sycommon/synacos/example.py +153 -0
- sycommon/synacos/example2.py +129 -0
- sycommon/synacos/feign.py +76 -410
- sycommon/synacos/feign_client.py +317 -0
- sycommon/synacos/nacos_service.py +146 -105
- sycommon/synacos/param.py +75 -0
- {sycommon_python_lib-0.1.21.dist-info → sycommon_python_lib-0.1.46.dist-info}/METADATA +11 -11
- {sycommon_python_lib-0.1.21.dist-info → sycommon_python_lib-0.1.46.dist-info}/RECORD +21 -16
- {sycommon_python_lib-0.1.21.dist-info → sycommon_python_lib-0.1.46.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.21.dist-info → sycommon_python_lib-0.1.46.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.21.dist-info → sycommon_python_lib-0.1.46.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
2
3
|
from typing import (
|
|
3
4
|
Callable, Coroutine, Dict, List, Optional, Type, Union, Any, Set
|
|
4
5
|
)
|
|
@@ -18,12 +19,14 @@ logger = SYLogger
|
|
|
18
19
|
class RabbitMQService:
|
|
19
20
|
"""
|
|
20
21
|
RabbitMQ服务封装,管理多个客户端实例,基于连接池实现资源复用
|
|
22
|
+
适配细粒度锁设计的RabbitMQClient,确保线程安全+高可用
|
|
21
23
|
"""
|
|
22
24
|
|
|
23
25
|
# 保存多个客户端实例
|
|
24
26
|
_clients: Dict[str, RabbitMQClient] = {}
|
|
25
27
|
# 保存消息处理器
|
|
26
|
-
_message_handlers: Dict[str, Callable
|
|
28
|
+
_message_handlers: Dict[str, Callable[[
|
|
29
|
+
MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]]] = {}
|
|
27
30
|
# 保存消费者任务
|
|
28
31
|
_consumer_tasks: Dict[str, asyncio.Task] = {}
|
|
29
32
|
# 保存配置信息
|
|
@@ -45,14 +48,26 @@ class RabbitMQService:
|
|
|
45
48
|
CONSUMER_START_TIMEOUT = 30 # 30秒超时
|
|
46
49
|
# 连接池实例
|
|
47
50
|
_connection_pool: Optional[RabbitMQConnectionPool] = None
|
|
51
|
+
# 服务关闭标记
|
|
52
|
+
_is_shutdown: bool = False
|
|
53
|
+
# 服务关闭锁
|
|
54
|
+
_shutdown_lock = asyncio.Lock()
|
|
55
|
+
# 连接状态监控任务
|
|
56
|
+
_connection_monitor_task: Optional[asyncio.Task] = None
|
|
57
|
+
# 重连配置
|
|
58
|
+
RECONNECT_INTERVAL = 15 # 重连基础间隔(秒)
|
|
59
|
+
MAX_RECONNECT_ATTEMPTS = 10 # 最大连续重连次数
|
|
48
60
|
|
|
49
61
|
@classmethod
|
|
50
62
|
def init(cls, config: dict, has_listeners: bool = False, has_senders: bool = False) -> Type['RabbitMQService']:
|
|
51
|
-
"""
|
|
52
|
-
初始化RabbitMQ服务(支持集群配置),同时创建连接池
|
|
53
|
-
"""
|
|
63
|
+
"""初始化RabbitMQ服务(支持集群配置),同时创建连接池"""
|
|
54
64
|
from sycommon.synacos.nacos_service import NacosService
|
|
55
65
|
|
|
66
|
+
# 防止重复初始化
|
|
67
|
+
if cls._config:
|
|
68
|
+
logger.warning("RabbitMQService已初始化,无需重复调用")
|
|
69
|
+
return cls
|
|
70
|
+
|
|
56
71
|
# 获取MQ配置
|
|
57
72
|
cls._config = NacosService(config).share_configs.get(
|
|
58
73
|
"mq.yml", {}).get('spring', {}).get('rabbitmq', {})
|
|
@@ -63,22 +78,28 @@ class RabbitMQService:
|
|
|
63
78
|
f"RabbitMQ服务初始化 - 集群节点: {cls._config.get('host')}, "
|
|
64
79
|
f"端口: {cls._config.get('port')}, "
|
|
65
80
|
f"虚拟主机: {cls._config.get('virtual-host')}, "
|
|
66
|
-
f"应用名: {cls._config.get('APP_NAME')}"
|
|
81
|
+
f"应用名: {cls._config.get('APP_NAME')}, "
|
|
82
|
+
f"心跳: {cls._config.get('heartbeat', 30)}s"
|
|
67
83
|
)
|
|
68
84
|
|
|
69
85
|
# 保存发送器和监听器存在状态
|
|
70
86
|
cls._has_listeners = has_listeners
|
|
71
87
|
cls._has_senders = has_senders
|
|
88
|
+
cls._is_shutdown = False
|
|
72
89
|
|
|
73
90
|
# 初始化连接池(在单独的异步方法中启动)
|
|
74
91
|
asyncio.create_task(cls._init_connection_pool())
|
|
75
92
|
|
|
93
|
+
# 启动连接监控任务(监听连接状态,自动重连)
|
|
94
|
+
cls._connection_monitor_task = asyncio.create_task(
|
|
95
|
+
cls._monitor_connections())
|
|
96
|
+
|
|
76
97
|
return cls
|
|
77
98
|
|
|
78
99
|
@classmethod
|
|
79
100
|
async def _init_connection_pool(cls):
|
|
80
|
-
"""
|
|
81
|
-
if cls._connection_pool or not cls._config:
|
|
101
|
+
"""初始化连接池(异步操作,带重试)"""
|
|
102
|
+
if cls._connection_pool or not cls._config or cls._is_shutdown:
|
|
82
103
|
return
|
|
83
104
|
|
|
84
105
|
try:
|
|
@@ -89,6 +110,8 @@ class RabbitMQService:
|
|
|
89
110
|
if not hosts_list:
|
|
90
111
|
raise ValueError("RabbitMQ集群配置为空,请检查host参数")
|
|
91
112
|
|
|
113
|
+
global_prefetch_count = cls._config.get('prefetch_count', 2)
|
|
114
|
+
|
|
92
115
|
# 创建连接池
|
|
93
116
|
cls._connection_pool = RabbitMQConnectionPool(
|
|
94
117
|
hosts=hosts_list,
|
|
@@ -96,38 +119,153 @@ class RabbitMQService:
|
|
|
96
119
|
username=cls._config.get('username', ""),
|
|
97
120
|
password=cls._config.get('password', ""),
|
|
98
121
|
virtualhost=cls._config.get('virtual-host', "/"),
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
channel_pool_size=cls._config.get(
|
|
102
|
-
'channel_pool_size', 10), # 通道池大小
|
|
103
|
-
heartbeat=cls._config.get('heartbeat', 30),
|
|
104
|
-
app_name=cls._config.get("APP_NAME", "")
|
|
122
|
+
app_name=cls._config.get("APP_NAME", ""),
|
|
123
|
+
prefetch_count=global_prefetch_count,
|
|
105
124
|
)
|
|
106
125
|
|
|
107
126
|
# 初始化连接池
|
|
108
|
-
await cls._connection_pool.init_pools()
|
|
127
|
+
await asyncio.wait_for(cls._connection_pool.init_pools(), timeout=30)
|
|
109
128
|
logger.info("RabbitMQ连接池初始化成功")
|
|
110
129
|
|
|
111
130
|
except Exception as e:
|
|
112
131
|
logger.error(f"RabbitMQ连接池初始化失败: {str(e)}", exc_info=True)
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
# 连接池初始化失败时重试(未关闭状态下)
|
|
133
|
+
if not cls._is_shutdown:
|
|
134
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
135
|
+
asyncio.create_task(cls._init_connection_pool())
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
async def _monitor_connections(cls):
|
|
139
|
+
"""连接监控任务:定期检查所有客户端连接状态,自动重连"""
|
|
140
|
+
logger.info("RabbitMQ连接监控任务启动")
|
|
141
|
+
while not cls._is_shutdown:
|
|
142
|
+
try:
|
|
143
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL) # 固定15秒间隔
|
|
144
|
+
|
|
145
|
+
# 跳过未初始化的连接池
|
|
146
|
+
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
# 检查所有客户端连接
|
|
150
|
+
# 使用list避免迭代中修改
|
|
151
|
+
for client_name, client in list(cls._clients.items()):
|
|
152
|
+
try:
|
|
153
|
+
# 双重校验连接状态(客户端内部校验 + 连接池连接校验)
|
|
154
|
+
client_connected = await client.is_connected
|
|
155
|
+
conn_connected = not (
|
|
156
|
+
client._channel_conn and client._channel_conn.is_closed)
|
|
157
|
+
|
|
158
|
+
if not client_connected or not conn_connected:
|
|
159
|
+
logger.warning(
|
|
160
|
+
f"客户端 '{client_name}' 连接异常 - 客户端状态: {client_connected}, "
|
|
161
|
+
f"连接状态: {conn_connected},触发自动重连"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# 重连前先清理无效资源
|
|
165
|
+
await cls._clean_client_resources(client)
|
|
166
|
+
|
|
167
|
+
# 执行重连(带重试)
|
|
168
|
+
reconnect_success = await cls._reconnect_client(client_name, client)
|
|
169
|
+
if reconnect_success:
|
|
170
|
+
logger.info(f"客户端 '{client_name}' 重连成功")
|
|
171
|
+
else:
|
|
172
|
+
logger.error(f"客户端 '{client_name}' 重连失败,将继续监控")
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(
|
|
176
|
+
f"监控客户端 '{client_name}' 连接状态失败: {str(e)}", exc_info=True)
|
|
177
|
+
|
|
178
|
+
# 检查连接池状态(如果连接池已关闭,重新初始化)
|
|
179
|
+
if not await cls._connection_pool.is_alive:
|
|
180
|
+
logger.error("RabbitMQ连接池已关闭,尝试重新初始化")
|
|
181
|
+
asyncio.create_task(cls._init_connection_pool())
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error("RabbitMQ连接监控任务异常", exc_info=True)
|
|
185
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL) # 异常后延迟重启监控
|
|
186
|
+
|
|
187
|
+
logger.info("RabbitMQ连接监控任务停止")
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
async def _clean_client_resources(cls, client: RabbitMQClient):
|
|
191
|
+
"""清理客户端无效资源(通道+连接)"""
|
|
192
|
+
try:
|
|
193
|
+
if client._channel and client._channel_conn:
|
|
194
|
+
# 先停止消费(避免消费中释放资源)
|
|
195
|
+
if client._consumer_tag:
|
|
196
|
+
await client.stop_consuming()
|
|
197
|
+
# 释放通道到连接池
|
|
198
|
+
await cls._connection_pool.release_channel(client._channel, client._channel_conn)
|
|
199
|
+
logger.debug("客户端无效资源释放成功")
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.warning(f"释放客户端无效资源失败: {str(e)}")
|
|
202
|
+
finally:
|
|
203
|
+
# 强制重置客户端状态
|
|
204
|
+
client._channel = None
|
|
205
|
+
client._channel_conn = None
|
|
206
|
+
client._exchange = None
|
|
207
|
+
client._queue = None
|
|
208
|
+
client._consumer_tag = None
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
async def _reconnect_client(cls, client_name: str, client: RabbitMQClient) -> bool:
|
|
212
|
+
"""客户端重连(带重试机制)"""
|
|
213
|
+
# 重连冷却,避免任务堆积
|
|
214
|
+
cooldown = cls.RECONNECT_INTERVAL * 2
|
|
215
|
+
await asyncio.sleep(cooldown)
|
|
216
|
+
|
|
217
|
+
for attempt in range(cls.MAX_RECONNECT_ATTEMPTS):
|
|
218
|
+
try:
|
|
219
|
+
# 执行重连
|
|
220
|
+
await client.connect()
|
|
221
|
+
|
|
222
|
+
# 验证重连结果(双重校验)
|
|
223
|
+
if await client.is_connected and client._queue:
|
|
224
|
+
# 如果是消费者,重新启动消费
|
|
225
|
+
if client_name in cls._message_handlers:
|
|
226
|
+
# 先停止旧的消费任务
|
|
227
|
+
if client_name in cls._consumer_tasks:
|
|
228
|
+
old_task = cls._consumer_tasks[client_name]
|
|
229
|
+
if not old_task.done():
|
|
230
|
+
old_task.cancel()
|
|
231
|
+
try:
|
|
232
|
+
await asyncio.wait_for(old_task, timeout=5)
|
|
233
|
+
except:
|
|
234
|
+
pass
|
|
235
|
+
# 启动新的消费任务
|
|
236
|
+
await cls.start_consumer(client_name)
|
|
237
|
+
return True
|
|
238
|
+
else:
|
|
239
|
+
logger.warning(
|
|
240
|
+
f"客户端 '{client_name}' 重连尝试 {attempt+1} 失败:资源未完全初始化")
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.error(
|
|
243
|
+
f"客户端 '{client_name}' 重连尝试 {attempt+1} 失败: {str(e)}", exc_info=True)
|
|
244
|
+
|
|
245
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
246
|
+
|
|
247
|
+
if not cls._is_shutdown:
|
|
248
|
+
asyncio.create_task(cls._reconnect_client(client_name, client))
|
|
249
|
+
return False
|
|
116
250
|
|
|
117
251
|
@classmethod
|
|
118
252
|
async def _create_client(cls, queue_name: str, **kwargs) -> RabbitMQClient:
|
|
119
|
-
|
|
253
|
+
"""创建客户端实例(适配新的RabbitMQClient API)"""
|
|
254
|
+
if cls._is_shutdown:
|
|
255
|
+
raise RuntimeError("RabbitMQService已关闭,无法创建客户端")
|
|
256
|
+
|
|
257
|
+
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
120
258
|
# 等待连接池初始化
|
|
121
259
|
start_time = asyncio.get_event_loop().time()
|
|
122
|
-
while not cls._connection_pool:
|
|
260
|
+
while not (cls._connection_pool and cls._connection_pool._initialized) and not cls._is_shutdown:
|
|
123
261
|
if asyncio.get_event_loop().time() - start_time > 30:
|
|
124
262
|
raise TimeoutError("等待连接池初始化超时")
|
|
125
263
|
await asyncio.sleep(1)
|
|
264
|
+
if cls._is_shutdown:
|
|
265
|
+
raise RuntimeError("服务关闭中,取消创建客户端")
|
|
126
266
|
|
|
127
267
|
app_name = kwargs.get('app_name', cls._config.get(
|
|
128
268
|
"APP_NAME", "")) if cls._config else ""
|
|
129
|
-
|
|
130
|
-
# 确定是否为发送器
|
|
131
269
|
is_sender = not cls._has_listeners
|
|
132
270
|
|
|
133
271
|
# 根据组件类型决定是否允许创建队列
|
|
@@ -142,12 +280,12 @@ class RabbitMQService:
|
|
|
142
280
|
else:
|
|
143
281
|
logger.info(f"监听器队列已包含app-name: {processed_queue_name}")
|
|
144
282
|
|
|
145
|
-
logger.
|
|
283
|
+
logger.info(
|
|
146
284
|
f"创建客户端 - 队列: {processed_queue_name}, 发送器: {is_sender}, "
|
|
147
285
|
f"允许创建: {create_if_not_exists}"
|
|
148
286
|
)
|
|
149
287
|
|
|
150
|
-
#
|
|
288
|
+
# 创建客户端实例(适配新的RabbitMQClient参数)
|
|
151
289
|
client = RabbitMQClient(
|
|
152
290
|
connection_pool=cls._connection_pool,
|
|
153
291
|
exchange_name=cls._config.get(
|
|
@@ -160,38 +298,41 @@ class RabbitMQService:
|
|
|
160
298
|
auto_delete=kwargs.get('auto_delete', False),
|
|
161
299
|
auto_parse_json=kwargs.get('auto_parse_json', True),
|
|
162
300
|
create_if_not_exists=create_if_not_exists,
|
|
163
|
-
connection_timeout=kwargs.get('connection_timeout', 10),
|
|
164
|
-
rpc_timeout=kwargs.get('rpc_timeout', 5),
|
|
165
|
-
reconnection_delay=kwargs.get('reconnection_delay', 1),
|
|
166
|
-
max_reconnection_attempts=kwargs.get(
|
|
167
|
-
'max_reconnection_attempts', 5),
|
|
168
301
|
prefetch_count=kwargs.get('prefetch_count', 2),
|
|
169
|
-
consumption_stall_threshold=kwargs.get(
|
|
170
|
-
'consumption_stall_threshold', 10)
|
|
171
302
|
)
|
|
172
303
|
|
|
173
|
-
#
|
|
174
|
-
await client.connect(
|
|
304
|
+
# 连接客户端
|
|
305
|
+
await client.connect()
|
|
306
|
+
|
|
307
|
+
# 监听器客户端连接后延迟1秒,确保消费状态就绪(仅首次启动)
|
|
308
|
+
if not is_sender and create_if_not_exists:
|
|
309
|
+
logger.info(
|
|
310
|
+
f"监听器客户端 '{processed_queue_name}' 连接成功,延迟1秒启动消费(解决启动时序问题)")
|
|
311
|
+
await asyncio.sleep(1)
|
|
312
|
+
|
|
175
313
|
return client
|
|
176
314
|
|
|
177
315
|
@classmethod
|
|
178
316
|
async def get_client(
|
|
179
317
|
cls,
|
|
180
|
-
client_name: str = "default", **
|
|
318
|
+
client_name: str = "default", **kwargs
|
|
181
319
|
) -> RabbitMQClient:
|
|
182
|
-
"""
|
|
183
|
-
|
|
184
|
-
|
|
320
|
+
"""获取或创建RabbitMQ客户端(基于连接池,线程安全)"""
|
|
321
|
+
if cls._is_shutdown:
|
|
322
|
+
raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
|
|
323
|
+
|
|
185
324
|
if not cls._config:
|
|
186
325
|
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
187
326
|
|
|
188
327
|
# 等待连接池就绪
|
|
189
|
-
if not cls._connection_pool:
|
|
328
|
+
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
190
329
|
start_time = asyncio.get_event_loop().time()
|
|
191
|
-
while not cls._connection_pool:
|
|
330
|
+
while not (cls._connection_pool and cls._connection_pool._initialized) and not cls._is_shutdown:
|
|
192
331
|
if asyncio.get_event_loop().time() - start_time > 30:
|
|
193
332
|
raise TimeoutError("等待连接池初始化超时")
|
|
194
333
|
await asyncio.sleep(1)
|
|
334
|
+
if cls._is_shutdown:
|
|
335
|
+
raise RuntimeError("服务关闭中,取消获取客户端")
|
|
195
336
|
|
|
196
337
|
# 确保锁存在
|
|
197
338
|
if client_name not in cls._init_locks:
|
|
@@ -204,18 +345,18 @@ class RabbitMQService:
|
|
|
204
345
|
is_sender = not cls._has_listeners or (
|
|
205
346
|
not kwargs.get('create_if_not_exists', True))
|
|
206
347
|
|
|
207
|
-
if client.is_connected:
|
|
348
|
+
if await client.is_connected:
|
|
208
349
|
# 如果是监听器但队列未初始化,重新连接
|
|
209
|
-
if not is_sender and not client.
|
|
210
|
-
logger.
|
|
350
|
+
if not is_sender and not client._queue:
|
|
351
|
+
logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
|
|
211
352
|
client.create_if_not_exists = True
|
|
212
|
-
await client.connect(
|
|
353
|
+
await client.connect()
|
|
213
354
|
return client
|
|
214
355
|
else:
|
|
215
|
-
logger.
|
|
356
|
+
logger.info(f"客户端 '{client_name}' 连接已关闭,重新连接")
|
|
216
357
|
if not is_sender:
|
|
217
358
|
client.create_if_not_exists = True
|
|
218
|
-
await client.connect(
|
|
359
|
+
await client.connect()
|
|
219
360
|
return client
|
|
220
361
|
|
|
221
362
|
# 创建新客户端
|
|
@@ -231,7 +372,6 @@ class RabbitMQService:
|
|
|
231
372
|
app_name=cls._config.get("APP_NAME", ""),
|
|
232
373
|
**kwargs
|
|
233
374
|
)
|
|
234
|
-
await client.connect(declare_queue=False)
|
|
235
375
|
cls._clients[client_name] = client
|
|
236
376
|
return client
|
|
237
377
|
|
|
@@ -240,11 +380,8 @@ class RabbitMQService:
|
|
|
240
380
|
|
|
241
381
|
# 检查队列是否已初始化
|
|
242
382
|
if initial_queue_name in cls._initialized_queues:
|
|
243
|
-
logger.
|
|
244
|
-
client = await cls._create_client(
|
|
245
|
-
initial_queue_name, ** kwargs
|
|
246
|
-
)
|
|
247
|
-
await client.connect(declare_queue=True)
|
|
383
|
+
logger.info(f"队列 '{initial_queue_name}' 已初始化,直接创建客户端")
|
|
384
|
+
client = await cls._create_client(initial_queue_name, **kwargs)
|
|
248
385
|
cls._clients[client_name] = client
|
|
249
386
|
return client
|
|
250
387
|
|
|
@@ -255,15 +392,12 @@ class RabbitMQService:
|
|
|
255
392
|
**kwargs
|
|
256
393
|
)
|
|
257
394
|
|
|
258
|
-
client.create_if_not_exists = True
|
|
259
|
-
await client.connect(declare_queue=True)
|
|
260
|
-
|
|
261
395
|
# 验证队列是否创建成功
|
|
262
|
-
if not client.
|
|
396
|
+
if not client._queue:
|
|
263
397
|
logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
|
|
264
398
|
client.create_if_not_exists = True
|
|
265
|
-
await client.connect(
|
|
266
|
-
if not client.
|
|
399
|
+
await client.connect()
|
|
400
|
+
if not client._queue:
|
|
267
401
|
raise Exception(f"无法创建队列 '{initial_queue_name}'")
|
|
268
402
|
|
|
269
403
|
# 记录已初始化的队列
|
|
@@ -274,10 +408,13 @@ class RabbitMQService:
|
|
|
274
408
|
cls._clients[client_name] = client
|
|
275
409
|
return client
|
|
276
410
|
|
|
277
|
-
# 以下方法逻辑与原有保持一致(无需修改)
|
|
278
411
|
@classmethod
|
|
279
|
-
async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False) -> None:
|
|
280
|
-
"""
|
|
412
|
+
async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False, **kwargs) -> None:
|
|
413
|
+
"""设置消息发送器(适配新客户端)"""
|
|
414
|
+
if cls._is_shutdown:
|
|
415
|
+
logger.warning("服务已关闭,无法设置发送器")
|
|
416
|
+
return
|
|
417
|
+
|
|
281
418
|
cls._has_senders = True
|
|
282
419
|
cls._has_listeners = has_listeners
|
|
283
420
|
logger.info(f"开始设置 {len(senders)} 个消息发送器")
|
|
@@ -287,6 +424,7 @@ class RabbitMQService:
|
|
|
287
424
|
if not sender_config.queue_name:
|
|
288
425
|
raise ValueError(f"发送器配置第{idx+1}项缺少queue_name")
|
|
289
426
|
|
|
427
|
+
prefetch_count = sender_config.prefetch_count
|
|
290
428
|
queue_name = sender_config.queue_name
|
|
291
429
|
app_name = cls._config.get(
|
|
292
430
|
"APP_NAME", "") if cls._config else ""
|
|
@@ -295,18 +433,18 @@ class RabbitMQService:
|
|
|
295
433
|
normalized_name = queue_name
|
|
296
434
|
if app_name and normalized_name.endswith(f".{app_name}"):
|
|
297
435
|
normalized_name = normalized_name[:-len(f".{app_name}")]
|
|
298
|
-
logger.
|
|
436
|
+
logger.info(f"发送器队列名称移除app-name后缀: {normalized_name}")
|
|
299
437
|
|
|
300
438
|
# 检查是否已初始化
|
|
301
439
|
if normalized_name in cls._sender_client_names:
|
|
302
|
-
logger.
|
|
440
|
+
logger.info(f"发送客户端 '{normalized_name}' 已存在,跳过")
|
|
303
441
|
continue
|
|
304
442
|
|
|
305
443
|
# 获取或创建客户端
|
|
306
444
|
if normalized_name in cls._clients:
|
|
307
445
|
client = cls._clients[normalized_name]
|
|
308
|
-
if not client.is_connected:
|
|
309
|
-
await client.connect(
|
|
446
|
+
if not await client.is_connected:
|
|
447
|
+
await client.connect()
|
|
310
448
|
else:
|
|
311
449
|
client = await cls.get_client(
|
|
312
450
|
client_name=normalized_name,
|
|
@@ -315,7 +453,9 @@ class RabbitMQService:
|
|
|
315
453
|
auto_delete=sender_config.auto_delete,
|
|
316
454
|
auto_parse_json=sender_config.auto_parse_json,
|
|
317
455
|
queue_name=queue_name,
|
|
318
|
-
create_if_not_exists=False
|
|
456
|
+
create_if_not_exists=False,
|
|
457
|
+
prefetch_count=prefetch_count,
|
|
458
|
+
**kwargs
|
|
319
459
|
)
|
|
320
460
|
|
|
321
461
|
# 记录客户端
|
|
@@ -334,8 +474,12 @@ class RabbitMQService:
|
|
|
334
474
|
logger.info(f"消息发送器设置完成,共 {len(cls._sender_client_names)} 个发送器")
|
|
335
475
|
|
|
336
476
|
@classmethod
|
|
337
|
-
async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False) -> None:
|
|
338
|
-
"""
|
|
477
|
+
async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False, **kwargs) -> None:
|
|
478
|
+
"""设置消息监听器(适配新客户端)"""
|
|
479
|
+
if cls._is_shutdown:
|
|
480
|
+
logger.warning("服务已关闭,无法设置监听器")
|
|
481
|
+
return
|
|
482
|
+
|
|
339
483
|
cls._has_listeners = True
|
|
340
484
|
cls._has_senders = has_senders
|
|
341
485
|
logger.info(f"开始设置 {len(listeners)} 个消息监听器")
|
|
@@ -345,9 +489,11 @@ class RabbitMQService:
|
|
|
345
489
|
# 转换配置并强制设置create_if_not_exists为True
|
|
346
490
|
listener_dict = listener_config.model_dump()
|
|
347
491
|
listener_dict['create_if_not_exists'] = True
|
|
492
|
+
listener_dict['prefetch_count'] = listener_config.prefetch_count
|
|
348
493
|
queue_name = listener_dict['queue_name']
|
|
349
494
|
|
|
350
|
-
logger.info(
|
|
495
|
+
logger.info(
|
|
496
|
+
f"设置监听器 {idx+1}/{len(listeners)}: {queue_name} (prefetch_count: {listener_config.prefetch_count})")
|
|
351
497
|
|
|
352
498
|
# 添加监听器
|
|
353
499
|
await cls.add_listener(**listener_dict)
|
|
@@ -372,7 +518,8 @@ class RabbitMQService:
|
|
|
372
518
|
running_clients = []
|
|
373
519
|
|
|
374
520
|
while len(running_clients) < len(required_clients) and \
|
|
375
|
-
(asyncio.get_event_loop().time() - start_time) < timeout
|
|
521
|
+
(asyncio.get_event_loop().time() - start_time) < timeout and \
|
|
522
|
+
not cls._is_shutdown:
|
|
376
523
|
|
|
377
524
|
running_clients = [
|
|
378
525
|
name for name, task in cls._consumer_tasks.items()
|
|
@@ -384,7 +531,7 @@ class RabbitMQService:
|
|
|
384
531
|
await asyncio.sleep(1)
|
|
385
532
|
|
|
386
533
|
failed_clients = [
|
|
387
|
-
name for name in required_clients if name not in running_clients]
|
|
534
|
+
name for name in required_clients if name not in running_clients and not cls._is_shutdown]
|
|
388
535
|
if failed_clients:
|
|
389
536
|
logger.error(f"以下消费者启动失败: {', '.join(failed_clients)}")
|
|
390
537
|
for client_name in failed_clients:
|
|
@@ -395,14 +542,18 @@ class RabbitMQService:
|
|
|
395
542
|
async def add_listener(
|
|
396
543
|
cls,
|
|
397
544
|
queue_name: str,
|
|
398
|
-
handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]], **
|
|
545
|
+
handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]], **kwargs
|
|
399
546
|
) -> None:
|
|
400
|
-
"""
|
|
547
|
+
"""添加消息监听器(线程安全)"""
|
|
548
|
+
if cls._is_shutdown:
|
|
549
|
+
logger.warning("服务已关闭,无法添加监听器")
|
|
550
|
+
return
|
|
551
|
+
|
|
401
552
|
if not cls._config:
|
|
402
553
|
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
403
554
|
|
|
404
555
|
if queue_name in cls._message_handlers:
|
|
405
|
-
logger.
|
|
556
|
+
logger.info(f"监听器 '{queue_name}' 已存在,跳过重复添加")
|
|
406
557
|
return
|
|
407
558
|
|
|
408
559
|
# 创建并初始化客户端
|
|
@@ -418,17 +569,35 @@ class RabbitMQService:
|
|
|
418
569
|
|
|
419
570
|
@classmethod
|
|
420
571
|
async def start_all_consumers(cls) -> None:
|
|
421
|
-
"""
|
|
572
|
+
"""启动所有已注册的消费者(线程安全)"""
|
|
573
|
+
if cls._is_shutdown:
|
|
574
|
+
logger.warning("服务已关闭,无法启动消费者")
|
|
575
|
+
return
|
|
576
|
+
|
|
422
577
|
for client_name in cls._message_handlers:
|
|
423
578
|
await cls.start_consumer(client_name)
|
|
424
579
|
|
|
425
580
|
@classmethod
|
|
426
581
|
async def start_consumer(cls, client_name: str) -> None:
|
|
427
582
|
"""启动指定客户端的消费者"""
|
|
428
|
-
if
|
|
429
|
-
logger.
|
|
583
|
+
if cls._is_shutdown:
|
|
584
|
+
logger.warning("服务已关闭,无法启动消费者")
|
|
430
585
|
return
|
|
431
586
|
|
|
587
|
+
# 检查任务状态,避免重复创建
|
|
588
|
+
if client_name in cls._consumer_tasks:
|
|
589
|
+
existing_task = cls._consumer_tasks[client_name]
|
|
590
|
+
if not existing_task.done():
|
|
591
|
+
# 检查任务是否处于异常状态,仅在异常时重启
|
|
592
|
+
if existing_task.exception() is not None:
|
|
593
|
+
logger.info(f"消费者 '{client_name}' 任务异常,重启")
|
|
594
|
+
existing_task.cancel()
|
|
595
|
+
else:
|
|
596
|
+
logger.info(f"消费者 '{client_name}' 已在运行中,无需重复启动")
|
|
597
|
+
return
|
|
598
|
+
else:
|
|
599
|
+
logger.info(f"消费者 '{client_name}' 任务已完成,重新启动")
|
|
600
|
+
|
|
432
601
|
if client_name not in cls._clients:
|
|
433
602
|
raise ValueError(f"RabbitMQ客户端 '{client_name}' 未初始化")
|
|
434
603
|
|
|
@@ -440,15 +609,22 @@ class RabbitMQService:
|
|
|
440
609
|
handler = cls.default_message_handler
|
|
441
610
|
|
|
442
611
|
# 设置消息处理器
|
|
443
|
-
client.set_message_handler(handler)
|
|
612
|
+
await client.set_message_handler(handler)
|
|
444
613
|
|
|
445
614
|
# 确保客户端已连接
|
|
446
615
|
start_time = asyncio.get_event_loop().time()
|
|
447
|
-
while not client.is_connected:
|
|
616
|
+
while not await client.is_connected and not cls._is_shutdown:
|
|
448
617
|
if asyncio.get_event_loop().time() - start_time > cls.CONSUMER_START_TIMEOUT:
|
|
449
618
|
raise TimeoutError(f"等待客户端 '{client_name}' 连接超时")
|
|
450
619
|
|
|
451
|
-
logger.
|
|
620
|
+
logger.info(f"等待客户端 '{client_name}' 连接就绪...")
|
|
621
|
+
await asyncio.sleep(1)
|
|
622
|
+
if cls._is_shutdown:
|
|
623
|
+
return
|
|
624
|
+
|
|
625
|
+
# 监听器启动消费前额外延迟1秒
|
|
626
|
+
if cls._has_listeners and not client_name.startswith("sender-"):
|
|
627
|
+
logger.info(f"消费者 '{client_name}' 准备启动,延迟1秒等待消费状态就绪")
|
|
452
628
|
await asyncio.sleep(1)
|
|
453
629
|
|
|
454
630
|
# 创建停止事件
|
|
@@ -463,8 +639,19 @@ class RabbitMQService:
|
|
|
463
639
|
attempt = 0
|
|
464
640
|
consumer_tag = None
|
|
465
641
|
|
|
466
|
-
while attempt < max_attempts and not stop_event.is_set():
|
|
642
|
+
while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
|
|
467
643
|
try:
|
|
644
|
+
# 启动消费前再次校验连接和队列状态
|
|
645
|
+
if not await client.is_connected:
|
|
646
|
+
logger.info(f"消费者 '{client_name}' 连接断开,尝试重连")
|
|
647
|
+
await client.connect()
|
|
648
|
+
|
|
649
|
+
# 确保队列和处理器已就绪
|
|
650
|
+
if not client._queue:
|
|
651
|
+
raise Exception("队列未初始化完成")
|
|
652
|
+
if not client._message_handler:
|
|
653
|
+
raise Exception("消息处理器未设置")
|
|
654
|
+
|
|
468
655
|
consumer_tag = await client.start_consuming()
|
|
469
656
|
if consumer_tag:
|
|
470
657
|
break
|
|
@@ -473,14 +660,18 @@ class RabbitMQService:
|
|
|
473
660
|
logger.warning(
|
|
474
661
|
f"启动消费者尝试 {attempt}/{max_attempts} 失败: {str(e)}")
|
|
475
662
|
if attempt < max_attempts:
|
|
476
|
-
await asyncio.sleep(
|
|
663
|
+
await asyncio.sleep(2)
|
|
664
|
+
|
|
665
|
+
if cls._is_shutdown:
|
|
666
|
+
return
|
|
477
667
|
|
|
478
668
|
if not consumer_tag:
|
|
479
669
|
raise Exception(f"经过 {max_attempts} 次尝试仍无法启动消费者")
|
|
480
670
|
|
|
481
671
|
# 记录消费者标签
|
|
482
672
|
cls._consumer_tags[client_name] = consumer_tag
|
|
483
|
-
logger.info(
|
|
673
|
+
logger.info(
|
|
674
|
+
f"消费者 '{client_name}' 开始消费,tag: {consumer_tag}")
|
|
484
675
|
|
|
485
676
|
# 等待停止事件
|
|
486
677
|
await stop_event.wait()
|
|
@@ -492,8 +683,9 @@ class RabbitMQService:
|
|
|
492
683
|
logger.error(
|
|
493
684
|
f"消费者 '{client_name}' 错误: {str(e)}", exc_info=True)
|
|
494
685
|
# 非主动停止时尝试重启
|
|
495
|
-
if not stop_event.is_set():
|
|
686
|
+
if not stop_event.is_set() and not cls._is_shutdown:
|
|
496
687
|
logger.info(f"尝试重启消费者 '{client_name}'")
|
|
688
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
497
689
|
asyncio.create_task(cls.start_consumer(client_name))
|
|
498
690
|
finally:
|
|
499
691
|
# 清理资源
|
|
@@ -522,8 +714,7 @@ class RabbitMQService:
|
|
|
522
714
|
t.result()
|
|
523
715
|
except Exception as e:
|
|
524
716
|
logger.error(f"消费者任务 '{client_name}' 异常结束: {str(e)}")
|
|
525
|
-
|
|
526
|
-
if client_name in cls._message_handlers: # 检查处理器是否仍存在
|
|
717
|
+
if client_name in cls._message_handlers and not cls._is_shutdown:
|
|
527
718
|
asyncio.create_task(cls.start_consumer(client_name))
|
|
528
719
|
|
|
529
720
|
task.add_done_callback(task_done_callback)
|
|
@@ -539,63 +730,90 @@ class RabbitMQService:
|
|
|
539
730
|
logger.info("===================\n")
|
|
540
731
|
|
|
541
732
|
@classmethod
|
|
542
|
-
def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
|
|
733
|
+
async def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
|
|
543
734
|
"""获取发送客户端"""
|
|
735
|
+
if cls._is_shutdown:
|
|
736
|
+
logger.warning("服务已关闭,无法获取发送器")
|
|
737
|
+
return None
|
|
738
|
+
|
|
544
739
|
if not queue_name:
|
|
545
740
|
logger.warning("发送器名称不能为空")
|
|
546
741
|
return None
|
|
547
742
|
|
|
548
743
|
# 检查是否在已注册的发送器中
|
|
549
744
|
if queue_name in cls._sender_client_names and queue_name in cls._clients:
|
|
550
|
-
|
|
745
|
+
client = cls._clients[queue_name]
|
|
746
|
+
if await client.is_connected:
|
|
747
|
+
return client
|
|
748
|
+
else:
|
|
749
|
+
logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
|
|
750
|
+
try:
|
|
751
|
+
await client.connect()
|
|
752
|
+
if await client.is_connected:
|
|
753
|
+
return client
|
|
754
|
+
except Exception as e:
|
|
755
|
+
logger.error(f"发送器 '{queue_name}' 重连失败: {str(e)}")
|
|
756
|
+
return None
|
|
551
757
|
|
|
552
758
|
# 检查是否带有app-name后缀
|
|
553
759
|
app_name = cls._config.get("APP_NAME", "") if cls._config else ""
|
|
554
|
-
if app_name
|
|
555
|
-
|
|
760
|
+
if app_name:
|
|
761
|
+
suffixed_name = f"{queue_name}.{app_name}"
|
|
762
|
+
if suffixed_name in cls._sender_client_names and suffixed_name in cls._clients:
|
|
763
|
+
client = cls._clients[suffixed_name]
|
|
764
|
+
if await client.is_connected:
|
|
765
|
+
return client
|
|
766
|
+
else:
|
|
767
|
+
logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
|
|
768
|
+
try:
|
|
769
|
+
await client.connect()
|
|
770
|
+
if await client.is_connected:
|
|
771
|
+
return client
|
|
772
|
+
except Exception as e:
|
|
773
|
+
logger.error(f"发送器 '{suffixed_name}' 重连失败: {str(e)}")
|
|
556
774
|
|
|
557
|
-
logger.
|
|
775
|
+
logger.info(f"未找到可用的发送器 '{queue_name}'")
|
|
558
776
|
return None
|
|
559
777
|
|
|
560
778
|
@classmethod
|
|
561
779
|
async def send_message(
|
|
562
780
|
cls,
|
|
563
781
|
data: Union[BaseModel, str, Dict[str, Any], None],
|
|
564
|
-
queue_name: str, **
|
|
782
|
+
queue_name: str, **kwargs
|
|
565
783
|
) -> None:
|
|
566
784
|
"""发送消息到指定队列"""
|
|
785
|
+
if cls._is_shutdown:
|
|
786
|
+
raise RuntimeError("RabbitMQService已关闭,无法发送消息")
|
|
787
|
+
|
|
567
788
|
# 获取发送客户端
|
|
568
|
-
sender = cls.get_sender(queue_name)
|
|
789
|
+
sender = await cls.get_sender(queue_name)
|
|
569
790
|
if not sender:
|
|
570
791
|
error_msg = f"未找到可用的RabbitMQ发送器 (queue_name: {queue_name})"
|
|
571
792
|
logger.error(error_msg)
|
|
572
793
|
raise ValueError(error_msg)
|
|
573
794
|
|
|
574
795
|
# 确保连接有效
|
|
575
|
-
if not sender.is_connected:
|
|
796
|
+
if not await sender.is_connected:
|
|
576
797
|
logger.info(f"发送器 '{queue_name}' 连接已关闭,尝试重新连接")
|
|
577
|
-
max_retry = 3
|
|
798
|
+
max_retry = 3
|
|
578
799
|
retry_count = 0
|
|
579
800
|
last_exception = None
|
|
580
801
|
|
|
581
|
-
while retry_count < max_retry:
|
|
802
|
+
while retry_count < max_retry and not cls._is_shutdown:
|
|
582
803
|
try:
|
|
583
|
-
|
|
584
|
-
await sender.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
804
|
+
await sender.connect()
|
|
805
|
+
if await sender.is_connected:
|
|
806
|
+
logger.info(
|
|
807
|
+
f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
|
|
808
|
+
break
|
|
588
809
|
except Exception as e:
|
|
589
810
|
last_exception = e
|
|
590
811
|
retry_count += 1
|
|
591
812
|
logger.warning(
|
|
592
|
-
f"发送器 '{queue_name}' 第 {retry_count} 次重连失败: {str(e)}"
|
|
593
|
-
)
|
|
594
|
-
if retry_count < max_retry:
|
|
595
|
-
await asyncio.sleep(1) # 重试前等待1秒
|
|
813
|
+
f"发送器 '{queue_name}' 第 {retry_count} 次重连失败: {str(e)}")
|
|
814
|
+
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
596
815
|
|
|
597
|
-
|
|
598
|
-
if retry_count >= max_retry and not sender.is_connected:
|
|
816
|
+
if retry_count >= max_retry and not await sender.is_connected:
|
|
599
817
|
error_msg = f"发送器 '{queue_name}' 经过 {max_retry} 次重连仍失败"
|
|
600
818
|
logger.error(f"{error_msg}: {str(last_exception)}")
|
|
601
819
|
raise Exception(error_msg) from last_exception
|
|
@@ -608,19 +826,18 @@ class RabbitMQService:
|
|
|
608
826
|
elif isinstance(data, BaseModel):
|
|
609
827
|
msg_content = data.model_dump_json()
|
|
610
828
|
elif isinstance(data, dict):
|
|
611
|
-
|
|
612
|
-
msg_content = json.dumps(data)
|
|
829
|
+
msg_content = json.dumps(data, ensure_ascii=False)
|
|
613
830
|
|
|
614
831
|
# 创建标准消息模型
|
|
615
832
|
mq_message = MQMsgModel(
|
|
616
833
|
topicCode=queue_name.split('.')[0] if queue_name else "",
|
|
617
834
|
msg=msg_content,
|
|
618
835
|
correlationDataId=kwargs.get(
|
|
619
|
-
'correlationDataId',
|
|
836
|
+
'correlationDataId', logger.get_trace_id()),
|
|
620
837
|
groupId=kwargs.get('groupId', ''),
|
|
621
838
|
dataKey=kwargs.get('dataKey', ""),
|
|
622
839
|
manualFlag=kwargs.get('manualFlag', False),
|
|
623
|
-
traceId=
|
|
840
|
+
traceId=logger.get_trace_id()
|
|
624
841
|
)
|
|
625
842
|
|
|
626
843
|
# 构建消息头
|
|
@@ -632,7 +849,7 @@ class RabbitMQService:
|
|
|
632
849
|
user_name="SYSTEM",
|
|
633
850
|
request_path="",
|
|
634
851
|
req_type="SYSTEM",
|
|
635
|
-
trace_id=
|
|
852
|
+
trace_id=logger.get_trace_id(),
|
|
636
853
|
).model_dump_json()
|
|
637
854
|
}
|
|
638
855
|
|
|
@@ -648,73 +865,63 @@ class RabbitMQService:
|
|
|
648
865
|
raise
|
|
649
866
|
|
|
650
867
|
@classmethod
|
|
651
|
-
async def shutdown(cls, timeout: float =
|
|
652
|
-
"""
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
logger.info(
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
868
|
+
async def shutdown(cls, timeout: float = 15.0) -> None:
|
|
869
|
+
"""优雅关闭所有资源(线程安全)"""
|
|
870
|
+
async with cls._shutdown_lock:
|
|
871
|
+
if cls._is_shutdown:
|
|
872
|
+
logger.info("RabbitMQService已关闭,无需重复操作")
|
|
873
|
+
return
|
|
874
|
+
|
|
875
|
+
cls._is_shutdown = True
|
|
876
|
+
logger.info("开始关闭RabbitMQ服务...")
|
|
877
|
+
|
|
878
|
+
# 1. 停止连接监控任务
|
|
879
|
+
if cls._connection_monitor_task and not cls._connection_monitor_task.done():
|
|
880
|
+
cls._connection_monitor_task.cancel()
|
|
881
|
+
try:
|
|
882
|
+
await asyncio.wait_for(cls._connection_monitor_task, timeout=timeout)
|
|
883
|
+
except asyncio.TimeoutError:
|
|
884
|
+
logger.warning("连接监控任务关闭超时")
|
|
885
|
+
except Exception as e:
|
|
886
|
+
logger.error(f"关闭连接监控任务失败: {str(e)}")
|
|
887
|
+
|
|
888
|
+
# 2. 停止所有消费者任务
|
|
889
|
+
for client_name, task in cls._consumer_tasks.items():
|
|
890
|
+
if not task.done():
|
|
891
|
+
# 触发停止事件
|
|
892
|
+
if client_name in cls._consumer_events:
|
|
893
|
+
cls._consumer_events[client_name].set()
|
|
894
|
+
# 取消任务
|
|
676
895
|
task.cancel()
|
|
677
896
|
try:
|
|
678
|
-
await task
|
|
679
|
-
except
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
except Exception as e:
|
|
683
|
-
logger.error(f"等待消费者任务完成时出错: {str(e)}")
|
|
684
|
-
|
|
685
|
-
# 关闭所有客户端连接
|
|
686
|
-
remaining_time = max(
|
|
687
|
-
0.0, timeout - (asyncio.get_event_loop().time() - start_time))
|
|
688
|
-
if remaining_time > 0 and cls._clients:
|
|
689
|
-
client_count = len(cls._clients)
|
|
690
|
-
client_timeout = remaining_time / client_count # 平均分配剩余时间
|
|
897
|
+
await asyncio.wait_for(task, timeout=timeout)
|
|
898
|
+
except Exception as e:
|
|
899
|
+
logger.error(f"关闭消费者 '{client_name}' 失败: {str(e)}")
|
|
691
900
|
|
|
692
|
-
|
|
901
|
+
# 3. 关闭所有客户端
|
|
902
|
+
for client in cls._clients.values():
|
|
693
903
|
try:
|
|
694
|
-
await
|
|
904
|
+
await client.close()
|
|
695
905
|
except Exception as e:
|
|
696
|
-
logger.
|
|
697
|
-
logger.info(f"客户端 '{name}' 已关闭")
|
|
906
|
+
logger.error(f"关闭客户端失败: {str(e)}")
|
|
698
907
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
logger.info("RabbitMQ服务已完全关闭")
|
|
908
|
+
# 4. 关闭连接池
|
|
909
|
+
if cls._connection_pool and cls._connection_pool._initialized:
|
|
910
|
+
try:
|
|
911
|
+
await cls._connection_pool.close()
|
|
912
|
+
logger.info("RabbitMQ连接池已关闭")
|
|
913
|
+
except Exception as e:
|
|
914
|
+
logger.error(f"关闭连接池失败: {str(e)}")
|
|
915
|
+
|
|
916
|
+
# 5. 清理状态
|
|
917
|
+
cls._clients.clear()
|
|
918
|
+
cls._message_handlers.clear()
|
|
919
|
+
cls._consumer_tasks.clear()
|
|
920
|
+
cls._consumer_events.clear()
|
|
921
|
+
cls._consumer_tags.clear()
|
|
922
|
+
cls._initialized_queues.clear()
|
|
923
|
+
cls._sender_client_names.clear()
|
|
924
|
+
cls._init_locks.clear()
|
|
925
|
+
cls._config = None
|
|
926
|
+
|
|
927
|
+
logger.info("RabbitMQService已完全关闭")
|