sycommon-python-lib 0.1.34__py3-none-any.whl → 0.1.35__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.
Potentially problematic release.
This version of sycommon-python-lib might be problematic. Click here for more details.
- sycommon/rabbitmq/rabbitmq_client.py +530 -271
- sycommon/rabbitmq/rabbitmq_service.py +213 -115
- sycommon/services.py +58 -26
- sycommon/synacos/feign.py +22 -9
- {sycommon_python_lib-0.1.34.dist-info → sycommon_python_lib-0.1.35.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.34.dist-info → sycommon_python_lib-0.1.35.dist-info}/RECORD +9 -9
- {sycommon_python_lib-0.1.34.dist-info → sycommon_python_lib-0.1.35.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.34.dist-info → sycommon_python_lib-0.1.35.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.34.dist-info → sycommon_python_lib-0.1.35.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,6 +19,7 @@ logger = SYLogger
|
|
|
18
19
|
class RabbitMQService:
|
|
19
20
|
"""
|
|
20
21
|
RabbitMQ服务封装,管理多个客户端实例,基于连接池实现资源复用
|
|
22
|
+
适配细粒度锁设计的RabbitMQClient,确保线程安全
|
|
21
23
|
"""
|
|
22
24
|
|
|
23
25
|
# 保存多个客户端实例
|
|
@@ -45,6 +47,10 @@ class RabbitMQService:
|
|
|
45
47
|
CONSUMER_START_TIMEOUT = 30 # 30秒超时
|
|
46
48
|
# 连接池实例
|
|
47
49
|
_connection_pool: Optional[RabbitMQConnectionPool] = None
|
|
50
|
+
# 服务关闭标记
|
|
51
|
+
_is_shutdown: bool = False
|
|
52
|
+
# 服务关闭锁
|
|
53
|
+
_shutdown_lock = asyncio.Lock()
|
|
48
54
|
|
|
49
55
|
@classmethod
|
|
50
56
|
def init(cls, config: dict, has_listeners: bool = False, has_senders: bool = False) -> Type['RabbitMQService']:
|
|
@@ -53,6 +59,11 @@ class RabbitMQService:
|
|
|
53
59
|
"""
|
|
54
60
|
from sycommon.synacos.nacos_service import NacosService
|
|
55
61
|
|
|
62
|
+
# 防止重复初始化
|
|
63
|
+
if cls._config:
|
|
64
|
+
logger.warning("RabbitMQService已初始化,无需重复调用")
|
|
65
|
+
return cls
|
|
66
|
+
|
|
56
67
|
# 获取MQ配置
|
|
57
68
|
cls._config = NacosService(config).share_configs.get(
|
|
58
69
|
"mq.yml", {}).get('spring', {}).get('rabbitmq', {})
|
|
@@ -69,6 +80,7 @@ class RabbitMQService:
|
|
|
69
80
|
# 保存发送器和监听器存在状态
|
|
70
81
|
cls._has_listeners = has_listeners
|
|
71
82
|
cls._has_senders = has_senders
|
|
83
|
+
cls._is_shutdown = False
|
|
72
84
|
|
|
73
85
|
# 初始化连接池(在单独的异步方法中启动)
|
|
74
86
|
asyncio.create_task(cls._init_connection_pool())
|
|
@@ -77,8 +89,8 @@ class RabbitMQService:
|
|
|
77
89
|
|
|
78
90
|
@classmethod
|
|
79
91
|
async def _init_connection_pool(cls):
|
|
80
|
-
"""
|
|
81
|
-
if cls._connection_pool or not cls._config:
|
|
92
|
+
"""初始化连接池(异步操作,带重试)"""
|
|
93
|
+
if cls._connection_pool or not cls._config or cls._is_shutdown:
|
|
82
94
|
return
|
|
83
95
|
|
|
84
96
|
try:
|
|
@@ -110,19 +122,26 @@ class RabbitMQService:
|
|
|
110
122
|
|
|
111
123
|
except Exception as e:
|
|
112
124
|
logger.error(f"RabbitMQ连接池初始化失败: {str(e)}", exc_info=True)
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
# 连接池初始化失败时重试(未关闭状态下)
|
|
126
|
+
if not cls._is_shutdown:
|
|
127
|
+
await asyncio.sleep(3)
|
|
128
|
+
asyncio.create_task(cls._init_connection_pool())
|
|
116
129
|
|
|
117
130
|
@classmethod
|
|
118
131
|
async def _create_client(cls, queue_name: str, **kwargs) -> RabbitMQClient:
|
|
132
|
+
"""创建客户端实例(适配新的RabbitMQClient API)"""
|
|
133
|
+
if cls._is_shutdown:
|
|
134
|
+
raise RuntimeError("RabbitMQService已关闭,无法创建客户端")
|
|
135
|
+
|
|
119
136
|
if not cls._connection_pool:
|
|
120
137
|
# 等待连接池初始化
|
|
121
138
|
start_time = asyncio.get_event_loop().time()
|
|
122
|
-
while not cls._connection_pool:
|
|
139
|
+
while not cls._connection_pool and not cls._is_shutdown:
|
|
123
140
|
if asyncio.get_event_loop().time() - start_time > 30:
|
|
124
141
|
raise TimeoutError("等待连接池初始化超时")
|
|
125
142
|
await asyncio.sleep(1)
|
|
143
|
+
if cls._is_shutdown:
|
|
144
|
+
raise RuntimeError("服务关闭中,取消创建客户端")
|
|
126
145
|
|
|
127
146
|
app_name = kwargs.get('app_name', cls._config.get(
|
|
128
147
|
"APP_NAME", "")) if cls._config else ""
|
|
@@ -147,7 +166,7 @@ class RabbitMQService:
|
|
|
147
166
|
f"允许创建: {create_if_not_exists}"
|
|
148
167
|
)
|
|
149
168
|
|
|
150
|
-
#
|
|
169
|
+
# 创建客户端实例(适配新的RabbitMQClient参数)
|
|
151
170
|
client = RabbitMQClient(
|
|
152
171
|
connection_pool=cls._connection_pool,
|
|
153
172
|
exchange_name=cls._config.get(
|
|
@@ -167,7 +186,7 @@ class RabbitMQService:
|
|
|
167
186
|
'max_reconnection_attempts', 5),
|
|
168
187
|
prefetch_count=kwargs.get('prefetch_count', 2),
|
|
169
188
|
consumption_stall_threshold=kwargs.get(
|
|
170
|
-
'consumption_stall_threshold',
|
|
189
|
+
'consumption_stall_threshold', 60), # 延长停滞阈值
|
|
171
190
|
)
|
|
172
191
|
|
|
173
192
|
# 使用declare_queue控制是否声明队列(发送器不声明,监听器声明)
|
|
@@ -180,18 +199,24 @@ class RabbitMQService:
|
|
|
180
199
|
client_name: str = "default", ** kwargs
|
|
181
200
|
) -> RabbitMQClient:
|
|
182
201
|
"""
|
|
183
|
-
获取或创建RabbitMQ
|
|
202
|
+
获取或创建RabbitMQ客户端(基于连接池,线程安全)
|
|
203
|
+
适配新的RabbitMQClient异步状态检查
|
|
184
204
|
"""
|
|
205
|
+
if cls._is_shutdown:
|
|
206
|
+
raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
|
|
207
|
+
|
|
185
208
|
if not cls._config:
|
|
186
209
|
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
187
210
|
|
|
188
211
|
# 等待连接池就绪
|
|
189
212
|
if not cls._connection_pool:
|
|
190
213
|
start_time = asyncio.get_event_loop().time()
|
|
191
|
-
while not cls._connection_pool:
|
|
214
|
+
while not cls._connection_pool and not cls._is_shutdown:
|
|
192
215
|
if asyncio.get_event_loop().time() - start_time > 30:
|
|
193
216
|
raise TimeoutError("等待连接池初始化超时")
|
|
194
217
|
await asyncio.sleep(1)
|
|
218
|
+
if cls._is_shutdown:
|
|
219
|
+
raise RuntimeError("服务关闭中,取消获取客户端")
|
|
195
220
|
|
|
196
221
|
# 确保锁存在
|
|
197
222
|
if client_name not in cls._init_locks:
|
|
@@ -204,12 +229,16 @@ class RabbitMQService:
|
|
|
204
229
|
is_sender = not cls._has_listeners or (
|
|
205
230
|
not kwargs.get('create_if_not_exists', True))
|
|
206
231
|
|
|
207
|
-
|
|
232
|
+
# 异步检查连接状态(适配新客户端的async属性)
|
|
233
|
+
if await client.is_connected:
|
|
208
234
|
# 如果是监听器但队列未初始化,重新连接
|
|
209
|
-
if not is_sender
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
235
|
+
if not is_sender:
|
|
236
|
+
# 异步获取队列(适配新客户端的原子方法)
|
|
237
|
+
_, _, queue = await client._get_connection_resources()
|
|
238
|
+
if not queue:
|
|
239
|
+
logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
|
|
240
|
+
client.create_if_not_exists = True
|
|
241
|
+
await client.connect(force_reconnect=True, declare_queue=True)
|
|
213
242
|
return client
|
|
214
243
|
else:
|
|
215
244
|
logger.info(f"客户端 '{client_name}' 连接已关闭,重新连接")
|
|
@@ -258,12 +287,14 @@ class RabbitMQService:
|
|
|
258
287
|
client.create_if_not_exists = True
|
|
259
288
|
await client.connect(declare_queue=True)
|
|
260
289
|
|
|
261
|
-
#
|
|
262
|
-
|
|
290
|
+
# 验证队列是否创建成功(异步获取队列)
|
|
291
|
+
_, _, queue = await client._get_connection_resources()
|
|
292
|
+
if not queue:
|
|
263
293
|
logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
|
|
264
294
|
client.create_if_not_exists = True
|
|
265
295
|
await client.connect(force_reconnect=True, declare_queue=True)
|
|
266
|
-
|
|
296
|
+
_, _, queue = await client._get_connection_resources()
|
|
297
|
+
if not queue:
|
|
267
298
|
raise Exception(f"无法创建队列 '{initial_queue_name}'")
|
|
268
299
|
|
|
269
300
|
# 记录已初始化的队列
|
|
@@ -274,10 +305,13 @@ class RabbitMQService:
|
|
|
274
305
|
cls._clients[client_name] = client
|
|
275
306
|
return client
|
|
276
307
|
|
|
277
|
-
# 以下方法逻辑与原有保持一致(无需修改)
|
|
278
308
|
@classmethod
|
|
279
309
|
async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False, ** kwargs) -> None:
|
|
280
|
-
"""
|
|
310
|
+
"""设置消息发送器(适配新客户端)"""
|
|
311
|
+
if cls._is_shutdown:
|
|
312
|
+
logger.warning("服务已关闭,无法设置发送器")
|
|
313
|
+
return
|
|
314
|
+
|
|
281
315
|
cls._has_senders = True
|
|
282
316
|
cls._has_listeners = has_listeners
|
|
283
317
|
logger.info(f"开始设置 {len(senders)} 个消息发送器")
|
|
@@ -306,7 +340,7 @@ class RabbitMQService:
|
|
|
306
340
|
# 获取或创建客户端
|
|
307
341
|
if normalized_name in cls._clients:
|
|
308
342
|
client = cls._clients[normalized_name]
|
|
309
|
-
if not client.is_connected:
|
|
343
|
+
if not await client.is_connected:
|
|
310
344
|
await client.connect(declare_queue=False)
|
|
311
345
|
else:
|
|
312
346
|
client = await cls.get_client(
|
|
@@ -338,7 +372,11 @@ class RabbitMQService:
|
|
|
338
372
|
|
|
339
373
|
@classmethod
|
|
340
374
|
async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False, ** kwargs) -> None:
|
|
341
|
-
"""
|
|
375
|
+
"""设置消息监听器(适配新客户端)"""
|
|
376
|
+
if cls._is_shutdown:
|
|
377
|
+
logger.warning("服务已关闭,无法设置监听器")
|
|
378
|
+
return
|
|
379
|
+
|
|
342
380
|
cls._has_listeners = True
|
|
343
381
|
cls._has_senders = has_senders
|
|
344
382
|
logger.info(f"开始设置 {len(listeners)} 个消息监听器")
|
|
@@ -369,13 +407,14 @@ class RabbitMQService:
|
|
|
369
407
|
|
|
370
408
|
@classmethod
|
|
371
409
|
async def _verify_consumers_started(cls, timeout: int = 30) -> None:
|
|
372
|
-
"""
|
|
410
|
+
"""验证消费者是否成功启动(适配新客户端的消费者标签管理)"""
|
|
373
411
|
start_time = asyncio.get_event_loop().time()
|
|
374
412
|
required_clients = list(cls._message_handlers.keys())
|
|
375
413
|
running_clients = []
|
|
376
414
|
|
|
377
415
|
while len(running_clients) < len(required_clients) and \
|
|
378
|
-
(asyncio.get_event_loop().time() - start_time) < timeout
|
|
416
|
+
(asyncio.get_event_loop().time() - start_time) < timeout and \
|
|
417
|
+
not cls._is_shutdown:
|
|
379
418
|
|
|
380
419
|
running_clients = [
|
|
381
420
|
name for name, task in cls._consumer_tasks.items()
|
|
@@ -387,7 +426,7 @@ class RabbitMQService:
|
|
|
387
426
|
await asyncio.sleep(1)
|
|
388
427
|
|
|
389
428
|
failed_clients = [
|
|
390
|
-
name for name in required_clients if name not in running_clients]
|
|
429
|
+
name for name in required_clients if name not in running_clients and not cls._is_shutdown]
|
|
391
430
|
if failed_clients:
|
|
392
431
|
logger.error(f"以下消费者启动失败: {', '.join(failed_clients)}")
|
|
393
432
|
for client_name in failed_clients:
|
|
@@ -400,7 +439,11 @@ class RabbitMQService:
|
|
|
400
439
|
queue_name: str,
|
|
401
440
|
handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]], ** kwargs
|
|
402
441
|
) -> None:
|
|
403
|
-
"""
|
|
442
|
+
"""添加消息监听器(线程安全)"""
|
|
443
|
+
if cls._is_shutdown:
|
|
444
|
+
logger.warning("服务已关闭,无法添加监听器")
|
|
445
|
+
return
|
|
446
|
+
|
|
404
447
|
if not cls._config:
|
|
405
448
|
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
406
449
|
|
|
@@ -421,13 +464,21 @@ class RabbitMQService:
|
|
|
421
464
|
|
|
422
465
|
@classmethod
|
|
423
466
|
async def start_all_consumers(cls) -> None:
|
|
424
|
-
"""
|
|
467
|
+
"""启动所有已注册的消费者(线程安全)"""
|
|
468
|
+
if cls._is_shutdown:
|
|
469
|
+
logger.warning("服务已关闭,无法启动消费者")
|
|
470
|
+
return
|
|
471
|
+
|
|
425
472
|
for client_name in cls._message_handlers:
|
|
426
473
|
await cls.start_consumer(client_name)
|
|
427
474
|
|
|
428
475
|
@classmethod
|
|
429
476
|
async def start_consumer(cls, client_name: str) -> None:
|
|
430
|
-
"""
|
|
477
|
+
"""启动指定客户端的消费者(适配新客户端的消费API)"""
|
|
478
|
+
if cls._is_shutdown:
|
|
479
|
+
logger.warning("服务已关闭,无法启动消费者")
|
|
480
|
+
return
|
|
481
|
+
|
|
431
482
|
if client_name in cls._consumer_tasks and not cls._consumer_tasks[client_name].done():
|
|
432
483
|
logger.info(f"消费者 '{client_name}' 已在运行中,无需重复启动")
|
|
433
484
|
return
|
|
@@ -442,17 +493,20 @@ class RabbitMQService:
|
|
|
442
493
|
logger.warning(f"未找到客户端 '{client_name}' 的处理器,使用默认处理器")
|
|
443
494
|
handler = cls.default_message_handler
|
|
444
495
|
|
|
445
|
-
#
|
|
496
|
+
# 设置消息处理器(适配新客户端的async方法)
|
|
446
497
|
await client.set_message_handler(handler)
|
|
447
498
|
|
|
448
|
-
#
|
|
499
|
+
# 确保客户端已连接(异步检查连接状态)
|
|
449
500
|
start_time = asyncio.get_event_loop().time()
|
|
450
|
-
while not client.is_connected:
|
|
501
|
+
while not await client.is_connected and not cls._is_shutdown:
|
|
451
502
|
if asyncio.get_event_loop().time() - start_time > cls.CONSUMER_START_TIMEOUT:
|
|
452
503
|
raise TimeoutError(f"等待客户端 '{client_name}' 连接超时")
|
|
453
504
|
|
|
454
505
|
logger.info(f"等待客户端 '{client_name}' 连接就绪...")
|
|
455
506
|
await asyncio.sleep(1)
|
|
507
|
+
if cls._is_shutdown:
|
|
508
|
+
logger.info("服务关闭中,取消启动消费者")
|
|
509
|
+
return
|
|
456
510
|
|
|
457
511
|
# 创建停止事件
|
|
458
512
|
stop_event = asyncio.Event()
|
|
@@ -466,7 +520,7 @@ class RabbitMQService:
|
|
|
466
520
|
attempt = 0
|
|
467
521
|
consumer_tag = None
|
|
468
522
|
|
|
469
|
-
while attempt < max_attempts and not stop_event.is_set():
|
|
523
|
+
while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
|
|
470
524
|
try:
|
|
471
525
|
consumer_tag = await client.start_consuming()
|
|
472
526
|
if consumer_tag:
|
|
@@ -478,6 +532,10 @@ class RabbitMQService:
|
|
|
478
532
|
if attempt < max_attempts:
|
|
479
533
|
await asyncio.sleep(1)
|
|
480
534
|
|
|
535
|
+
if cls._is_shutdown:
|
|
536
|
+
logger.info("服务关闭中,消费者启动中止")
|
|
537
|
+
return
|
|
538
|
+
|
|
481
539
|
if not consumer_tag:
|
|
482
540
|
raise Exception(f"经过 {max_attempts} 次尝试仍无法启动消费者")
|
|
483
541
|
|
|
@@ -495,8 +553,10 @@ class RabbitMQService:
|
|
|
495
553
|
logger.error(
|
|
496
554
|
f"消费者 '{client_name}' 错误: {str(e)}", exc_info=True)
|
|
497
555
|
# 非主动停止时尝试重启
|
|
498
|
-
if not stop_event.is_set():
|
|
556
|
+
if not stop_event.is_set() and not cls._is_shutdown:
|
|
499
557
|
logger.info(f"尝试重启消费者 '{client_name}'")
|
|
558
|
+
# 延迟重启,避免频繁重试
|
|
559
|
+
await asyncio.sleep(3)
|
|
500
560
|
asyncio.create_task(cls.start_consumer(client_name))
|
|
501
561
|
finally:
|
|
502
562
|
# 清理资源
|
|
@@ -505,7 +565,7 @@ class RabbitMQService:
|
|
|
505
565
|
except Exception as e:
|
|
506
566
|
logger.error(f"停止消费者 '{client_name}' 时出错: {str(e)}")
|
|
507
567
|
|
|
508
|
-
#
|
|
568
|
+
# 移除状态记录(线程安全)
|
|
509
569
|
if client_name in cls._consumer_tags:
|
|
510
570
|
del cls._consumer_tags[client_name]
|
|
511
571
|
if client_name in cls._consumer_events:
|
|
@@ -526,7 +586,7 @@ class RabbitMQService:
|
|
|
526
586
|
except Exception as e:
|
|
527
587
|
logger.error(f"消费者任务 '{client_name}' 异常结束: {str(e)}")
|
|
528
588
|
# 任务异常时自动重启(如果服务未关闭)
|
|
529
|
-
if client_name in cls._message_handlers:
|
|
589
|
+
if client_name in cls._message_handlers and not cls._is_shutdown:
|
|
530
590
|
asyncio.create_task(cls.start_consumer(client_name))
|
|
531
591
|
|
|
532
592
|
task.add_done_callback(task_done_callback)
|
|
@@ -542,22 +602,50 @@ class RabbitMQService:
|
|
|
542
602
|
logger.info("===================\n")
|
|
543
603
|
|
|
544
604
|
@classmethod
|
|
545
|
-
def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
|
|
546
|
-
"""
|
|
605
|
+
async def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
|
|
606
|
+
"""获取发送客户端(异步检查连接状态)"""
|
|
607
|
+
if cls._is_shutdown:
|
|
608
|
+
logger.warning("服务已关闭,无法获取发送器")
|
|
609
|
+
return None
|
|
610
|
+
|
|
547
611
|
if not queue_name:
|
|
548
612
|
logger.warning("发送器名称不能为空")
|
|
549
613
|
return None
|
|
550
614
|
|
|
551
615
|
# 检查是否在已注册的发送器中
|
|
552
616
|
if queue_name in cls._sender_client_names and queue_name in cls._clients:
|
|
553
|
-
|
|
617
|
+
client = cls._clients[queue_name]
|
|
618
|
+
# 异步检查连接状态
|
|
619
|
+
if await client.is_connected:
|
|
620
|
+
return client
|
|
621
|
+
else:
|
|
622
|
+
logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
|
|
623
|
+
try:
|
|
624
|
+
await client.connect(declare_queue=False)
|
|
625
|
+
if await client.is_connected:
|
|
626
|
+
return client
|
|
627
|
+
except Exception as e:
|
|
628
|
+
logger.error(f"发送器 '{queue_name}' 重连失败: {str(e)}")
|
|
629
|
+
return None
|
|
554
630
|
|
|
555
631
|
# 检查是否带有app-name后缀
|
|
556
632
|
app_name = cls._config.get("APP_NAME", "") if cls._config else ""
|
|
557
|
-
if app_name
|
|
558
|
-
|
|
633
|
+
if app_name:
|
|
634
|
+
suffixed_name = f"{queue_name}.{app_name}"
|
|
635
|
+
if suffixed_name in cls._sender_client_names and suffixed_name in cls._clients:
|
|
636
|
+
client = cls._clients[suffixed_name]
|
|
637
|
+
if await client.is_connected:
|
|
638
|
+
return client
|
|
639
|
+
else:
|
|
640
|
+
logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
|
|
641
|
+
try:
|
|
642
|
+
await client.connect(declare_queue=False)
|
|
643
|
+
if await client.is_connected:
|
|
644
|
+
return client
|
|
645
|
+
except Exception as e:
|
|
646
|
+
logger.error(f"发送器 '{suffixed_name}' 重连失败: {str(e)}")
|
|
559
647
|
|
|
560
|
-
logger.info(f"
|
|
648
|
+
logger.info(f"未找到可用的发送器 '{queue_name}'")
|
|
561
649
|
return None
|
|
562
650
|
|
|
563
651
|
@classmethod
|
|
@@ -566,28 +654,32 @@ class RabbitMQService:
|
|
|
566
654
|
data: Union[BaseModel, str, Dict[str, Any], None],
|
|
567
655
|
queue_name: str, ** kwargs
|
|
568
656
|
) -> None:
|
|
569
|
-
"""
|
|
570
|
-
|
|
571
|
-
|
|
657
|
+
"""发送消息到指定队列(适配新客户端的publish API)"""
|
|
658
|
+
if cls._is_shutdown:
|
|
659
|
+
raise RuntimeError("RabbitMQService已关闭,无法发送消息")
|
|
660
|
+
|
|
661
|
+
# 获取发送客户端(异步方法)
|
|
662
|
+
sender = await cls.get_sender(queue_name)
|
|
572
663
|
if not sender:
|
|
573
664
|
error_msg = f"未找到可用的RabbitMQ发送器 (queue_name: {queue_name})"
|
|
574
665
|
logger.error(error_msg)
|
|
575
666
|
raise ValueError(error_msg)
|
|
576
667
|
|
|
577
668
|
# 确保连接有效
|
|
578
|
-
if not sender.is_connected:
|
|
669
|
+
if not await sender.is_connected:
|
|
579
670
|
logger.info(f"发送器 '{queue_name}' 连接已关闭,尝试重新连接")
|
|
580
671
|
max_retry = 3 # 最大重试次数
|
|
581
672
|
retry_count = 0
|
|
582
673
|
last_exception = None
|
|
583
674
|
|
|
584
|
-
while retry_count < max_retry:
|
|
675
|
+
while retry_count < max_retry and not cls._is_shutdown:
|
|
585
676
|
try:
|
|
586
677
|
# 尝试重连,每次重试间隔1秒
|
|
587
678
|
await sender.connect(force_reconnect=True, declare_queue=False)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
679
|
+
if await sender.is_connected:
|
|
680
|
+
logger.info(
|
|
681
|
+
f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
|
|
682
|
+
break # 重连成功则退出循环
|
|
591
683
|
except Exception as e:
|
|
592
684
|
last_exception = e
|
|
593
685
|
retry_count += 1
|
|
@@ -598,7 +690,7 @@ class RabbitMQService:
|
|
|
598
690
|
await asyncio.sleep(1) # 重试前等待1秒
|
|
599
691
|
|
|
600
692
|
# 所有重试都失败则抛出异常
|
|
601
|
-
if retry_count >= max_retry and not sender.is_connected:
|
|
693
|
+
if retry_count >= max_retry and not await sender.is_connected:
|
|
602
694
|
error_msg = f"发送器 '{queue_name}' 经过 {max_retry} 次重连仍失败"
|
|
603
695
|
logger.error(f"{error_msg}: {str(last_exception)}")
|
|
604
696
|
raise Exception(error_msg) from last_exception
|
|
@@ -611,8 +703,7 @@ class RabbitMQService:
|
|
|
611
703
|
elif isinstance(data, BaseModel):
|
|
612
704
|
msg_content = data.model_dump_json()
|
|
613
705
|
elif isinstance(data, dict):
|
|
614
|
-
|
|
615
|
-
msg_content = json.dumps(data)
|
|
706
|
+
msg_content = json.dumps(data, ensure_ascii=False)
|
|
616
707
|
|
|
617
708
|
# 创建标准消息模型
|
|
618
709
|
mq_message = MQMsgModel(
|
|
@@ -639,7 +730,7 @@ class RabbitMQService:
|
|
|
639
730
|
).model_dump_json()
|
|
640
731
|
}
|
|
641
732
|
|
|
642
|
-
#
|
|
733
|
+
# 发送消息(适配新客户端的publish方法)
|
|
643
734
|
await sender.publish(
|
|
644
735
|
message_body=mq_message.model_dump_json(),
|
|
645
736
|
headers=mq_header,
|
|
@@ -652,72 +743,79 @@ class RabbitMQService:
|
|
|
652
743
|
|
|
653
744
|
@classmethod
|
|
654
745
|
async def shutdown(cls, timeout: float = 15.0) -> None:
|
|
655
|
-
"""
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
logger.info(
|
|
746
|
+
"""优雅关闭所有资源(线程安全,适配新客户端的close API)"""
|
|
747
|
+
async with cls._shutdown_lock:
|
|
748
|
+
if cls._is_shutdown:
|
|
749
|
+
logger.info("RabbitMQService已关闭,无需重复操作")
|
|
750
|
+
return
|
|
751
|
+
|
|
752
|
+
cls._is_shutdown = True
|
|
753
|
+
logger.info("开始关闭RabbitMQ服务...")
|
|
754
|
+
|
|
755
|
+
# 发送停止信号给所有消费者
|
|
756
|
+
for client_name, event in cls._consumer_events.items():
|
|
757
|
+
event.set()
|
|
758
|
+
logger.info(f"已向消费者 '{client_name}' 发送退出信号")
|
|
759
|
+
|
|
760
|
+
# 等待消费者任务完成
|
|
761
|
+
remaining_time = max(
|
|
762
|
+
0.0, timeout - (asyncio.get_event_loop().time() - asyncio.get_event_loop().time()))
|
|
763
|
+
if remaining_time > 0 and cls._consumer_tasks:
|
|
764
|
+
try:
|
|
765
|
+
done, pending = await asyncio.wait(
|
|
766
|
+
list(cls._consumer_tasks.values()),
|
|
767
|
+
timeout=remaining_time,
|
|
768
|
+
return_when=asyncio.ALL_COMPLETED
|
|
769
|
+
)
|
|
663
770
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
)
|
|
771
|
+
# 处理超时的任务
|
|
772
|
+
for task in pending:
|
|
773
|
+
task_name = task.get_name()
|
|
774
|
+
logger.warning(f"任务 '{task_name}' 关闭超时,强制取消")
|
|
775
|
+
task.cancel()
|
|
776
|
+
try:
|
|
777
|
+
await task
|
|
778
|
+
except (asyncio.CancelledError, RuntimeError):
|
|
779
|
+
pass
|
|
674
780
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
task_name = task.get_name()
|
|
678
|
-
logger.warning(f"任务 '{task_name}' 关闭超时,强制取消")
|
|
679
|
-
task.cancel()
|
|
680
|
-
try:
|
|
681
|
-
await task
|
|
682
|
-
except (asyncio.CancelledError, RuntimeError):
|
|
683
|
-
pass
|
|
781
|
+
except Exception as e:
|
|
782
|
+
logger.error(f"等待消费者任务完成时出错: {str(e)}")
|
|
684
783
|
|
|
685
|
-
|
|
686
|
-
|
|
784
|
+
# 关闭所有客户端连接(适配新客户端的async close方法)
|
|
785
|
+
remaining_time = max(
|
|
786
|
+
0.0, timeout - (asyncio.get_event_loop().time() - asyncio.get_event_loop().time()))
|
|
787
|
+
if remaining_time > 0 and cls._clients:
|
|
788
|
+
client_count = len(cls._clients)
|
|
789
|
+
client_timeout = remaining_time / client_count # 平均分配剩余时间
|
|
687
790
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
791
|
+
for name, client in cls._clients.items():
|
|
792
|
+
try:
|
|
793
|
+
if await client.is_connected:
|
|
794
|
+
await asyncio.wait_for(client.close(), timeout=client_timeout)
|
|
795
|
+
except Exception as e:
|
|
796
|
+
logger.warning(f"关闭客户端 '{name}' 时出错: {str(e)}")
|
|
797
|
+
logger.info(f"客户端 '{name}' 已关闭")
|
|
694
798
|
|
|
695
|
-
|
|
799
|
+
# 关闭连接池
|
|
800
|
+
if cls._connection_pool:
|
|
696
801
|
try:
|
|
697
|
-
await
|
|
802
|
+
await cls._connection_pool.close()
|
|
803
|
+
logger.info("RabbitMQ连接池已关闭")
|
|
698
804
|
except Exception as e:
|
|
699
|
-
logger.warning(f"
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
cls._consumer_tags.clear()
|
|
717
|
-
cls._initialized_queues.clear()
|
|
718
|
-
cls._init_locks.clear()
|
|
719
|
-
cls._has_listeners = False
|
|
720
|
-
cls._has_senders = False
|
|
721
|
-
cls._connection_pool = None
|
|
722
|
-
|
|
723
|
-
logger.info("RabbitMQ服务已完全关闭")
|
|
805
|
+
logger.warning(f"关闭连接池时出错: {str(e)}")
|
|
806
|
+
|
|
807
|
+
# 清理所有状态
|
|
808
|
+
cls._clients.clear()
|
|
809
|
+
cls._consumer_tasks.clear()
|
|
810
|
+
cls._message_handlers.clear()
|
|
811
|
+
cls._sender_client_names.clear()
|
|
812
|
+
cls._consumer_events.clear()
|
|
813
|
+
cls._consumer_tags.clear()
|
|
814
|
+
cls._initialized_queues.clear()
|
|
815
|
+
cls._init_locks.clear()
|
|
816
|
+
cls._has_listeners = False
|
|
817
|
+
cls._has_senders = False
|
|
818
|
+
cls._connection_pool = None
|
|
819
|
+
cls._config = None
|
|
820
|
+
|
|
821
|
+
logger.info("RabbitMQ服务已完全关闭")
|