sycommon-python-lib 0.1.29__py3-none-any.whl → 0.1.38__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,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:
@@ -105,24 +117,31 @@ class RabbitMQService:
105
117
  )
106
118
 
107
119
  # 初始化连接池
108
- await cls._connection_pool.init_pools()
120
+ await asyncio.wait_for(cls._connection_pool.init_pools(), timeout=30)
109
121
  logger.info("RabbitMQ连接池初始化成功")
110
122
 
111
123
  except Exception as e:
112
124
  logger.error(f"RabbitMQ连接池初始化失败: {str(e)}", exc_info=True)
113
- # 连接池初始化失败时重试
114
- await asyncio.sleep(1)
115
- asyncio.create_task(cls._init_connection_pool())
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 ""
@@ -142,12 +161,12 @@ class RabbitMQService:
142
161
  else:
143
162
  logger.info(f"监听器队列已包含app-name: {processed_queue_name}")
144
163
 
145
- logger.debug(
164
+ logger.info(
146
165
  f"创建客户端 - 队列: {processed_queue_name}, 发送器: {is_sender}, "
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(
@@ -161,17 +180,24 @@ class RabbitMQService:
161
180
  auto_parse_json=kwargs.get('auto_parse_json', True),
162
181
  create_if_not_exists=create_if_not_exists,
163
182
  connection_timeout=kwargs.get('connection_timeout', 10),
164
- rpc_timeout=kwargs.get('rpc_timeout', 5),
183
+ rpc_timeout=kwargs.get('rpc_timeout', 10),
165
184
  reconnection_delay=kwargs.get('reconnection_delay', 1),
166
185
  max_reconnection_attempts=kwargs.get(
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', 10)
189
+ 'consumption_stall_threshold', 60), # 延长停滞阈值
171
190
  )
172
191
 
173
192
  # 使用declare_queue控制是否声明队列(发送器不声明,监听器声明)
174
193
  await client.connect(declare_queue=not is_sender)
194
+
195
+ # 监听器客户端连接后延迟1秒,确保消费状态就绪(仅首次启动)
196
+ if not is_sender and create_if_not_exists:
197
+ logger.info(
198
+ f"监听器客户端 '{processed_queue_name}' 连接成功,延迟1秒启动消费(解决启动时序问题)")
199
+ await asyncio.sleep(1)
200
+
175
201
  return client
176
202
 
177
203
  @classmethod
@@ -180,18 +206,24 @@ class RabbitMQService:
180
206
  client_name: str = "default", ** kwargs
181
207
  ) -> RabbitMQClient:
182
208
  """
183
- 获取或创建RabbitMQ客户端(基于连接池)
209
+ 获取或创建RabbitMQ客户端(基于连接池,线程安全)
210
+ 适配新的RabbitMQClient异步状态检查
184
211
  """
212
+ if cls._is_shutdown:
213
+ raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
214
+
185
215
  if not cls._config:
186
216
  raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
187
217
 
188
218
  # 等待连接池就绪
189
219
  if not cls._connection_pool:
190
220
  start_time = asyncio.get_event_loop().time()
191
- while not cls._connection_pool:
221
+ while not cls._connection_pool and not cls._is_shutdown:
192
222
  if asyncio.get_event_loop().time() - start_time > 30:
193
223
  raise TimeoutError("等待连接池初始化超时")
194
224
  await asyncio.sleep(1)
225
+ if cls._is_shutdown:
226
+ raise RuntimeError("服务关闭中,取消获取客户端")
195
227
 
196
228
  # 确保锁存在
197
229
  if client_name not in cls._init_locks:
@@ -204,15 +236,19 @@ class RabbitMQService:
204
236
  is_sender = not cls._has_listeners or (
205
237
  not kwargs.get('create_if_not_exists', True))
206
238
 
207
- if client.is_connected:
239
+ # 异步检查连接状态(适配新客户端的async属性)
240
+ if await client.is_connected:
208
241
  # 如果是监听器但队列未初始化,重新连接
209
- if not is_sender and not client.queue:
210
- logger.debug(f"客户端 '{client_name}' 队列未初始化,重新连接")
211
- client.create_if_not_exists = True
212
- await client.connect(force_reconnect=True, declare_queue=True)
242
+ if not is_sender:
243
+ # 异步获取队列(适配新客户端的原子方法)
244
+ _, _, queue = await client._get_connection_resources()
245
+ if not queue:
246
+ logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
247
+ client.create_if_not_exists = True
248
+ await client.connect(force_reconnect=True, declare_queue=True)
213
249
  return client
214
250
  else:
215
- logger.debug(f"客户端 '{client_name}' 连接已关闭,重新连接")
251
+ logger.info(f"客户端 '{client_name}' 连接已关闭,重新连接")
216
252
  if not is_sender:
217
253
  client.create_if_not_exists = True
218
254
  await client.connect(declare_queue=not is_sender)
@@ -240,7 +276,7 @@ class RabbitMQService:
240
276
 
241
277
  # 检查队列是否已初始化
242
278
  if initial_queue_name in cls._initialized_queues:
243
- logger.debug(f"队列 '{initial_queue_name}' 已初始化,直接创建客户端")
279
+ logger.info(f"队列 '{initial_queue_name}' 已初始化,直接创建客户端")
244
280
  client = await cls._create_client(
245
281
  initial_queue_name, ** kwargs
246
282
  )
@@ -258,12 +294,14 @@ class RabbitMQService:
258
294
  client.create_if_not_exists = True
259
295
  await client.connect(declare_queue=True)
260
296
 
261
- # 验证队列是否创建成功
262
- if not client.queue:
297
+ # 验证队列是否创建成功(异步获取队列)
298
+ _, _, queue = await client._get_connection_resources()
299
+ if not queue:
263
300
  logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
264
301
  client.create_if_not_exists = True
265
302
  await client.connect(force_reconnect=True, declare_queue=True)
266
- if not client.queue:
303
+ _, _, queue = await client._get_connection_resources()
304
+ if not queue:
267
305
  raise Exception(f"无法创建队列 '{initial_queue_name}'")
268
306
 
269
307
  # 记录已初始化的队列
@@ -274,10 +312,13 @@ class RabbitMQService:
274
312
  cls._clients[client_name] = client
275
313
  return client
276
314
 
277
- # 以下方法逻辑与原有保持一致(无需修改)
278
315
  @classmethod
279
- async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False) -> None:
280
- """设置消息发送器"""
316
+ async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False, ** kwargs) -> None:
317
+ """设置消息发送器(适配新客户端)"""
318
+ if cls._is_shutdown:
319
+ logger.warning("服务已关闭,无法设置发送器")
320
+ return
321
+
281
322
  cls._has_senders = True
282
323
  cls._has_listeners = has_listeners
283
324
  logger.info(f"开始设置 {len(senders)} 个消息发送器")
@@ -287,6 +328,7 @@ class RabbitMQService:
287
328
  if not sender_config.queue_name:
288
329
  raise ValueError(f"发送器配置第{idx+1}项缺少queue_name")
289
330
 
331
+ prefetch_count = sender_config.prefetch_count
290
332
  queue_name = sender_config.queue_name
291
333
  app_name = cls._config.get(
292
334
  "APP_NAME", "") if cls._config else ""
@@ -295,17 +337,17 @@ class RabbitMQService:
295
337
  normalized_name = queue_name
296
338
  if app_name and normalized_name.endswith(f".{app_name}"):
297
339
  normalized_name = normalized_name[:-len(f".{app_name}")]
298
- logger.debug(f"发送器队列名称移除app-name后缀: {normalized_name}")
340
+ logger.info(f"发送器队列名称移除app-name后缀: {normalized_name}")
299
341
 
300
342
  # 检查是否已初始化
301
343
  if normalized_name in cls._sender_client_names:
302
- logger.debug(f"发送客户端 '{normalized_name}' 已存在,跳过")
344
+ logger.info(f"发送客户端 '{normalized_name}' 已存在,跳过")
303
345
  continue
304
346
 
305
347
  # 获取或创建客户端
306
348
  if normalized_name in cls._clients:
307
349
  client = cls._clients[normalized_name]
308
- if not client.is_connected:
350
+ if not await client.is_connected:
309
351
  await client.connect(declare_queue=False)
310
352
  else:
311
353
  client = await cls.get_client(
@@ -315,7 +357,9 @@ class RabbitMQService:
315
357
  auto_delete=sender_config.auto_delete,
316
358
  auto_parse_json=sender_config.auto_parse_json,
317
359
  queue_name=queue_name,
318
- create_if_not_exists=False
360
+ create_if_not_exists=False,
361
+ prefetch_count=prefetch_count,
362
+ ** kwargs
319
363
  )
320
364
 
321
365
  # 记录客户端
@@ -334,8 +378,12 @@ class RabbitMQService:
334
378
  logger.info(f"消息发送器设置完成,共 {len(cls._sender_client_names)} 个发送器")
335
379
 
336
380
  @classmethod
337
- async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False) -> None:
338
- """设置消息监听器"""
381
+ async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False, ** kwargs) -> None:
382
+ """设置消息监听器(适配新客户端)"""
383
+ if cls._is_shutdown:
384
+ logger.warning("服务已关闭,无法设置监听器")
385
+ return
386
+
339
387
  cls._has_listeners = True
340
388
  cls._has_senders = has_senders
341
389
  logger.info(f"开始设置 {len(listeners)} 个消息监听器")
@@ -366,13 +414,14 @@ class RabbitMQService:
366
414
 
367
415
  @classmethod
368
416
  async def _verify_consumers_started(cls, timeout: int = 30) -> None:
369
- """验证消费者是否成功启动"""
417
+ """验证消费者是否成功启动(适配新客户端的消费者标签管理)"""
370
418
  start_time = asyncio.get_event_loop().time()
371
419
  required_clients = list(cls._message_handlers.keys())
372
420
  running_clients = []
373
421
 
374
422
  while len(running_clients) < len(required_clients) and \
375
- (asyncio.get_event_loop().time() - start_time) < timeout:
423
+ (asyncio.get_event_loop().time() - start_time) < timeout and \
424
+ not cls._is_shutdown:
376
425
 
377
426
  running_clients = [
378
427
  name for name, task in cls._consumer_tasks.items()
@@ -384,7 +433,7 @@ class RabbitMQService:
384
433
  await asyncio.sleep(1)
385
434
 
386
435
  failed_clients = [
387
- name for name in required_clients if name not in running_clients]
436
+ name for name in required_clients if name not in running_clients and not cls._is_shutdown]
388
437
  if failed_clients:
389
438
  logger.error(f"以下消费者启动失败: {', '.join(failed_clients)}")
390
439
  for client_name in failed_clients:
@@ -397,12 +446,16 @@ class RabbitMQService:
397
446
  queue_name: str,
398
447
  handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]], ** kwargs
399
448
  ) -> None:
400
- """添加消息监听器"""
449
+ """添加消息监听器(线程安全)"""
450
+ if cls._is_shutdown:
451
+ logger.warning("服务已关闭,无法添加监听器")
452
+ return
453
+
401
454
  if not cls._config:
402
455
  raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
403
456
 
404
457
  if queue_name in cls._message_handlers:
405
- logger.debug(f"监听器 '{queue_name}' 已存在,跳过重复添加")
458
+ logger.info(f"监听器 '{queue_name}' 已存在,跳过重复添加")
406
459
  return
407
460
 
408
461
  # 创建并初始化客户端
@@ -418,15 +471,23 @@ class RabbitMQService:
418
471
 
419
472
  @classmethod
420
473
  async def start_all_consumers(cls) -> None:
421
- """启动所有已注册的消费者"""
474
+ """启动所有已注册的消费者(线程安全)"""
475
+ if cls._is_shutdown:
476
+ logger.warning("服务已关闭,无法启动消费者")
477
+ return
478
+
422
479
  for client_name in cls._message_handlers:
423
480
  await cls.start_consumer(client_name)
424
481
 
425
482
  @classmethod
426
483
  async def start_consumer(cls, client_name: str) -> None:
427
- """启动指定客户端的消费者"""
484
+ """启动指定客户端的消费者(适配新客户端的消费API)"""
485
+ if cls._is_shutdown:
486
+ logger.warning("服务已关闭,无法启动消费者")
487
+ return
488
+
428
489
  if client_name in cls._consumer_tasks and not cls._consumer_tasks[client_name].done():
429
- logger.debug(f"消费者 '{client_name}' 已在运行中,无需重复启动")
490
+ logger.info(f"消费者 '{client_name}' 已在运行中,无需重复启动")
430
491
  return
431
492
 
432
493
  if client_name not in cls._clients:
@@ -439,16 +500,24 @@ class RabbitMQService:
439
500
  logger.warning(f"未找到客户端 '{client_name}' 的处理器,使用默认处理器")
440
501
  handler = cls.default_message_handler
441
502
 
442
- # 设置消息处理器
443
- client.set_message_handler(handler)
503
+ # 设置消息处理器(适配新客户端的async方法)
504
+ await client.set_message_handler(handler)
444
505
 
445
- # 确保客户端已连接
506
+ # 确保客户端已连接(异步检查连接状态)
446
507
  start_time = asyncio.get_event_loop().time()
447
- while not client.is_connected:
508
+ while not await client.is_connected and not cls._is_shutdown:
448
509
  if asyncio.get_event_loop().time() - start_time > cls.CONSUMER_START_TIMEOUT:
449
510
  raise TimeoutError(f"等待客户端 '{client_name}' 连接超时")
450
511
 
451
- logger.debug(f"等待客户端 '{client_name}' 连接就绪...")
512
+ logger.info(f"等待客户端 '{client_name}' 连接就绪...")
513
+ await asyncio.sleep(1)
514
+ if cls._is_shutdown:
515
+ logger.info("服务关闭中,取消启动消费者")
516
+ return
517
+
518
+ # 监听器启动消费前额外延迟1秒,确保consumer_tag完全生成(不删消息)
519
+ if cls._has_listeners and not client_name.startswith("sender-"):
520
+ logger.info(f"消费者 '{client_name}' 准备启动,延迟1秒等待消费状态就绪(不删除积压消息)")
452
521
  await asyncio.sleep(1)
453
522
 
454
523
  # 创建停止事件
@@ -463,8 +532,18 @@ class RabbitMQService:
463
532
  attempt = 0
464
533
  consumer_tag = None
465
534
 
466
- while attempt < max_attempts and not stop_event.is_set():
535
+ while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
467
536
  try:
537
+ # 启动消费前再次校验连接和队列状态
538
+ if not await client.is_connected:
539
+ logger.info(f"消费者 '{client_name}' 连接断开,尝试重连")
540
+ await client.connect(force_reconnect=True, declare_queue=True)
541
+
542
+ # 确保队列已就绪
543
+ _, _, queue = await client._get_connection_resources()
544
+ if not queue:
545
+ raise Exception("队列未初始化完成")
546
+
468
547
  consumer_tag = await client.start_consuming()
469
548
  if consumer_tag:
470
549
  break
@@ -475,12 +554,17 @@ class RabbitMQService:
475
554
  if attempt < max_attempts:
476
555
  await asyncio.sleep(1)
477
556
 
557
+ if cls._is_shutdown:
558
+ logger.info("服务关闭中,消费者启动中止")
559
+ return
560
+
478
561
  if not consumer_tag:
479
562
  raise Exception(f"经过 {max_attempts} 次尝试仍无法启动消费者")
480
563
 
481
564
  # 记录消费者标签
482
565
  cls._consumer_tags[client_name] = consumer_tag
483
- logger.info(f"消费者 '{client_name}' 开始消费,tag: {consumer_tag}")
566
+ logger.info(
567
+ f"消费者 '{client_name}' 开始消费,tag: {consumer_tag}(将处理队列中所有积压消息)")
484
568
 
485
569
  # 等待停止事件
486
570
  await stop_event.wait()
@@ -492,8 +576,10 @@ class RabbitMQService:
492
576
  logger.error(
493
577
  f"消费者 '{client_name}' 错误: {str(e)}", exc_info=True)
494
578
  # 非主动停止时尝试重启
495
- if not stop_event.is_set():
579
+ if not stop_event.is_set() and not cls._is_shutdown:
496
580
  logger.info(f"尝试重启消费者 '{client_name}'")
581
+ # 延迟重启,避免频繁重试
582
+ await asyncio.sleep(3)
497
583
  asyncio.create_task(cls.start_consumer(client_name))
498
584
  finally:
499
585
  # 清理资源
@@ -502,7 +588,7 @@ class RabbitMQService:
502
588
  except Exception as e:
503
589
  logger.error(f"停止消费者 '{client_name}' 时出错: {str(e)}")
504
590
 
505
- # 移除状态记录
591
+ # 移除状态记录(线程安全)
506
592
  if client_name in cls._consumer_tags:
507
593
  del cls._consumer_tags[client_name]
508
594
  if client_name in cls._consumer_events:
@@ -523,7 +609,7 @@ class RabbitMQService:
523
609
  except Exception as e:
524
610
  logger.error(f"消费者任务 '{client_name}' 异常结束: {str(e)}")
525
611
  # 任务异常时自动重启(如果服务未关闭)
526
- if client_name in cls._message_handlers: # 检查处理器是否仍存在
612
+ if client_name in cls._message_handlers and not cls._is_shutdown:
527
613
  asyncio.create_task(cls.start_consumer(client_name))
528
614
 
529
615
  task.add_done_callback(task_done_callback)
@@ -539,22 +625,50 @@ class RabbitMQService:
539
625
  logger.info("===================\n")
540
626
 
541
627
  @classmethod
542
- def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
543
- """获取发送客户端"""
628
+ async def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
629
+ """获取发送客户端(异步检查连接状态)"""
630
+ if cls._is_shutdown:
631
+ logger.warning("服务已关闭,无法获取发送器")
632
+ return None
633
+
544
634
  if not queue_name:
545
635
  logger.warning("发送器名称不能为空")
546
636
  return None
547
637
 
548
638
  # 检查是否在已注册的发送器中
549
639
  if queue_name in cls._sender_client_names and queue_name in cls._clients:
550
- return cls._clients[queue_name]
640
+ client = cls._clients[queue_name]
641
+ # 异步检查连接状态
642
+ if await client.is_connected:
643
+ return client
644
+ else:
645
+ logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
646
+ try:
647
+ await client.connect(declare_queue=False)
648
+ if await client.is_connected:
649
+ return client
650
+ except Exception as e:
651
+ logger.error(f"发送器 '{queue_name}' 重连失败: {str(e)}")
652
+ return None
551
653
 
552
654
  # 检查是否带有app-name后缀
553
655
  app_name = cls._config.get("APP_NAME", "") if cls._config else ""
554
- if app_name and f"{queue_name}.{app_name}" in cls._sender_client_names:
555
- return cls._clients.get(f"{queue_name}.{app_name}")
656
+ if app_name:
657
+ suffixed_name = f"{queue_name}.{app_name}"
658
+ if suffixed_name in cls._sender_client_names and suffixed_name in cls._clients:
659
+ client = cls._clients[suffixed_name]
660
+ if await client.is_connected:
661
+ return client
662
+ else:
663
+ logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
664
+ try:
665
+ await client.connect(declare_queue=False)
666
+ if await client.is_connected:
667
+ return client
668
+ except Exception as e:
669
+ logger.error(f"发送器 '{suffixed_name}' 重连失败: {str(e)}")
556
670
 
557
- logger.debug(f"未找到发送器 '{queue_name}'")
671
+ logger.info(f"未找到可用的发送器 '{queue_name}'")
558
672
  return None
559
673
 
560
674
  @classmethod
@@ -563,28 +677,32 @@ class RabbitMQService:
563
677
  data: Union[BaseModel, str, Dict[str, Any], None],
564
678
  queue_name: str, ** kwargs
565
679
  ) -> None:
566
- """发送消息到指定队列"""
567
- # 获取发送客户端
568
- sender = cls.get_sender(queue_name)
680
+ """发送消息到指定队列(适配新客户端的publish API)"""
681
+ if cls._is_shutdown:
682
+ raise RuntimeError("RabbitMQService已关闭,无法发送消息")
683
+
684
+ # 获取发送客户端(异步方法)
685
+ sender = await cls.get_sender(queue_name)
569
686
  if not sender:
570
687
  error_msg = f"未找到可用的RabbitMQ发送器 (queue_name: {queue_name})"
571
688
  logger.error(error_msg)
572
689
  raise ValueError(error_msg)
573
690
 
574
691
  # 确保连接有效
575
- if not sender.is_connected:
692
+ if not await sender.is_connected:
576
693
  logger.info(f"发送器 '{queue_name}' 连接已关闭,尝试重新连接")
577
694
  max_retry = 3 # 最大重试次数
578
695
  retry_count = 0
579
696
  last_exception = None
580
697
 
581
- while retry_count < max_retry:
698
+ while retry_count < max_retry and not cls._is_shutdown:
582
699
  try:
583
700
  # 尝试重连,每次重试间隔1秒
584
701
  await sender.connect(force_reconnect=True, declare_queue=False)
585
- logger.info(
586
- f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
587
- break # 重连成功则退出循环
702
+ if await sender.is_connected:
703
+ logger.info(
704
+ f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
705
+ break # 重连成功则退出循环
588
706
  except Exception as e:
589
707
  last_exception = e
590
708
  retry_count += 1
@@ -595,7 +713,7 @@ class RabbitMQService:
595
713
  await asyncio.sleep(1) # 重试前等待1秒
596
714
 
597
715
  # 所有重试都失败则抛出异常
598
- if retry_count >= max_retry and not sender.is_connected:
716
+ if retry_count >= max_retry and not await sender.is_connected:
599
717
  error_msg = f"发送器 '{queue_name}' 经过 {max_retry} 次重连仍失败"
600
718
  logger.error(f"{error_msg}: {str(last_exception)}")
601
719
  raise Exception(error_msg) from last_exception
@@ -608,8 +726,7 @@ class RabbitMQService:
608
726
  elif isinstance(data, BaseModel):
609
727
  msg_content = data.model_dump_json()
610
728
  elif isinstance(data, dict):
611
- import json
612
- msg_content = json.dumps(data)
729
+ msg_content = json.dumps(data, ensure_ascii=False)
613
730
 
614
731
  # 创建标准消息模型
615
732
  mq_message = MQMsgModel(
@@ -636,7 +753,7 @@ class RabbitMQService:
636
753
  ).model_dump_json()
637
754
  }
638
755
 
639
- # 发送消息
756
+ # 发送消息(适配新客户端的publish方法)
640
757
  await sender.publish(
641
758
  message_body=mq_message.model_dump_json(),
642
759
  headers=mq_header,
@@ -648,73 +765,80 @@ class RabbitMQService:
648
765
  raise
649
766
 
650
767
  @classmethod
651
- async def shutdown(cls, timeout: float = 10.0) -> None:
652
- """优雅关闭所有资源(新增连接池关闭逻辑)"""
653
- start_time = asyncio.get_event_loop().time()
654
- logger.info("开始关闭RabbitMQ服务...")
768
+ async def shutdown(cls, timeout: float = 15.0) -> None:
769
+ """优雅关闭所有资源(线程安全,适配新客户端的close API)"""
770
+ async with cls._shutdown_lock:
771
+ if cls._is_shutdown:
772
+ logger.info("RabbitMQService已关闭,无需重复操作")
773
+ return
774
+
775
+ cls._is_shutdown = True
776
+ logger.info("开始关闭RabbitMQ服务...")
777
+
778
+ # 发送停止信号给所有消费者
779
+ for client_name, event in cls._consumer_events.items():
780
+ event.set()
781
+ logger.info(f"已向消费者 '{client_name}' 发送退出信号")
782
+
783
+ # 等待消费者任务完成
784
+ remaining_time = max(
785
+ 0.0, timeout - (asyncio.get_event_loop().time() - asyncio.get_event_loop().time()))
786
+ if remaining_time > 0 and cls._consumer_tasks:
787
+ try:
788
+ done, pending = await asyncio.wait(
789
+ list(cls._consumer_tasks.values()),
790
+ timeout=remaining_time,
791
+ return_when=asyncio.ALL_COMPLETED
792
+ )
655
793
 
656
- # 发送停止信号给所有消费者
657
- for client_name, event in cls._consumer_events.items():
658
- event.set()
659
- logger.info(f"已向消费者 '{client_name}' 发送退出信号")
794
+ # 处理超时的任务
795
+ for task in pending:
796
+ task_name = task.get_name()
797
+ logger.warning(f"任务 '{task_name}' 关闭超时,强制取消")
798
+ task.cancel()
799
+ try:
800
+ await task
801
+ except (asyncio.CancelledError, RuntimeError):
802
+ pass
660
803
 
661
- # 等待消费者任务完成
662
- remaining_time = max(
663
- 0.0, timeout - (asyncio.get_event_loop().time() - start_time))
664
- if remaining_time > 0 and cls._consumer_tasks:
665
- try:
666
- done, pending = await asyncio.wait(
667
- list(cls._consumer_tasks.values()),
668
- timeout=remaining_time,
669
- return_when=asyncio.ALL_COMPLETED
670
- )
804
+ except Exception as e:
805
+ logger.error(f"等待消费者任务完成时出错: {str(e)}")
671
806
 
672
- # 处理超时的任务
673
- for task in pending:
674
- task_name = task.get_name()
675
- logger.warning(f"任务 '{task_name}' 关闭超时,强制取消")
676
- task.cancel()
677
- try:
678
- await task
679
- except (asyncio.CancelledError, RuntimeError):
680
- pass
807
+ # 关闭所有客户端连接(适配新客户端的async close方法)
808
+ remaining_time = max(
809
+ 0.0, timeout - (asyncio.get_event_loop().time() - asyncio.get_event_loop().time()))
810
+ if remaining_time > 0 and cls._clients:
811
+ client_count = len(cls._clients)
812
+ client_timeout = remaining_time / client_count # 平均分配剩余时间
681
813
 
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 # 平均分配剩余时间
814
+ for name, client in cls._clients.items():
815
+ try:
816
+ if await client.is_connected:
817
+ await asyncio.wait_for(client.close(), timeout=client_timeout)
818
+ except Exception as e:
819
+ logger.warning(f"关闭客户端 '{name}' 时出错: {str(e)}")
820
+ logger.info(f"客户端 '{name}' 已关闭")
691
821
 
692
- for name, client in cls._clients.items():
822
+ # 关闭连接池
823
+ if cls._connection_pool:
693
824
  try:
694
- await asyncio.wait_for(client.close(), timeout=client_timeout)
825
+ await cls._connection_pool.close()
826
+ logger.info("RabbitMQ连接池已关闭")
695
827
  except Exception as e:
696
- logger.warning(f"关闭客户端 '{name}' 时出错: {str(e)}")
697
- logger.info(f"客户端 '{name}' 已关闭")
698
-
699
- # 关闭连接池
700
- if cls._connection_pool:
701
- try:
702
- await cls._connection_pool.close()
703
- logger.info("RabbitMQ连接池已关闭")
704
- except Exception as e:
705
- logger.warning(f"关闭连接池时出错: {str(e)}")
706
-
707
- # 清理所有状态
708
- cls._clients.clear()
709
- cls._consumer_tasks.clear()
710
- cls._message_handlers.clear()
711
- cls._sender_client_names.clear()
712
- cls._consumer_events.clear()
713
- cls._consumer_tags.clear()
714
- cls._initialized_queues.clear()
715
- cls._init_locks.clear()
716
- cls._has_listeners = False
717
- cls._has_senders = False
718
- cls._connection_pool = None
719
-
720
- logger.info("RabbitMQ服务已完全关闭")
828
+ logger.warning(f"关闭连接池时出错: {str(e)}")
829
+
830
+ # 清理所有状态
831
+ cls._clients.clear()
832
+ cls._consumer_tasks.clear()
833
+ cls._message_handlers.clear()
834
+ cls._sender_client_names.clear()
835
+ cls._consumer_events.clear()
836
+ cls._consumer_tags.clear()
837
+ cls._initialized_queues.clear()
838
+ cls._init_locks.clear()
839
+ cls._has_listeners = False
840
+ cls._has_senders = False
841
+ cls._connection_pool = None
842
+ cls._config = None
843
+
844
+ logger.info("RabbitMQ服务已完全关闭")