sycommon-python-lib 0.1.33__py3-none-any.whl → 0.1.34__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 +68 -37
- sycommon/rabbitmq/rabbitmq_service.py +1 -1
- {sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/RECORD +7 -7
- {sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/top_level.txt +0 -0
|
@@ -80,7 +80,7 @@ class RabbitMQClient:
|
|
|
80
80
|
self._exchange_exists = False
|
|
81
81
|
self._queue_exists = False
|
|
82
82
|
self._queue_bound = False
|
|
83
|
-
self._is_consuming = False
|
|
83
|
+
self._is_consuming = False
|
|
84
84
|
self._closed = False
|
|
85
85
|
self._consumer_tag: Optional[ConsumerTag] = None
|
|
86
86
|
self._last_activity_timestamp = asyncio.get_event_loop().time()
|
|
@@ -98,6 +98,8 @@ class RabbitMQClient:
|
|
|
98
98
|
|
|
99
99
|
# 消息处理跟踪
|
|
100
100
|
self._tracking_messages: Dict[str, Dict[str, Any]] = {}
|
|
101
|
+
# 状态保护锁
|
|
102
|
+
self._state_lock = asyncio.Lock()
|
|
101
103
|
|
|
102
104
|
@property
|
|
103
105
|
def is_connected(self) -> bool:
|
|
@@ -115,11 +117,12 @@ class RabbitMQClient:
|
|
|
115
117
|
"""更新最后消息处理时间戳"""
|
|
116
118
|
self._last_message_processed = asyncio.get_event_loop().time()
|
|
117
119
|
|
|
118
|
-
def _set_is_consuming(self, value: bool) -> None:
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
async def _set_is_consuming(self, value: bool) -> None:
|
|
121
|
+
"""安全更新消费状态并记录日志(带锁保护)"""
|
|
122
|
+
async with self._state_lock:
|
|
123
|
+
if self._is_consuming != value:
|
|
124
|
+
logger.info(f"消费状态变更: {self._is_consuming} → {value}")
|
|
125
|
+
self._is_consuming = value
|
|
123
126
|
|
|
124
127
|
async def _get_channel(self) -> AbstractChannel:
|
|
125
128
|
"""从通道池获取通道(使用上下文管理器)"""
|
|
@@ -200,7 +203,7 @@ class RabbitMQClient:
|
|
|
200
203
|
self._exchange_exists = False
|
|
201
204
|
self._queue_exists = False
|
|
202
205
|
self._queue_bound = False
|
|
203
|
-
self._set_is_consuming(False)
|
|
206
|
+
await self._set_is_consuming(False)
|
|
204
207
|
|
|
205
208
|
retries = 0
|
|
206
209
|
last_exception = None
|
|
@@ -312,7 +315,6 @@ class RabbitMQClient:
|
|
|
312
315
|
current_time = asyncio.get_event_loop().time()
|
|
313
316
|
# 清理消息跟踪记录
|
|
314
317
|
if self._tracking_messages:
|
|
315
|
-
# 1. 清理已确认的消息
|
|
316
318
|
acked_ids = [
|
|
317
319
|
msg_id for msg_id, info in self._tracking_messages.items()
|
|
318
320
|
if info.get('acked', False)
|
|
@@ -322,28 +324,38 @@ class RabbitMQClient:
|
|
|
322
324
|
if acked_ids:
|
|
323
325
|
logger.info(f"清理了 {len(acked_ids)} 条已确认消息记录")
|
|
324
326
|
|
|
325
|
-
#
|
|
327
|
+
# 检查消费停滞(仅当消费状态为 True 时才处理)
|
|
326
328
|
if self._is_consuming:
|
|
327
329
|
if current_time - self._last_message_processed > self.consumption_stall_threshold:
|
|
328
330
|
if self._tracking_messages:
|
|
329
331
|
logger.warning(
|
|
330
332
|
f"消费停滞,但有 {len(self._tracking_messages)} 个消息正在处理,暂不重启")
|
|
331
333
|
else:
|
|
332
|
-
# 无有效消息,重启消费
|
|
333
334
|
logger.info("消费停滞且无消息处理,重启消费")
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
335
|
+
# 重启消费时增加异常捕获,确保状态回滚
|
|
336
|
+
try:
|
|
337
|
+
await self.stop_consuming()
|
|
338
|
+
await asyncio.sleep(1)
|
|
339
|
+
# 重启前检查处理器是否存在
|
|
340
|
+
if self.message_handler:
|
|
341
|
+
await self.start_consuming()
|
|
342
|
+
else:
|
|
343
|
+
logger.error("消费处理器已丢失,无法重启消费")
|
|
344
|
+
except Exception as e:
|
|
345
|
+
logger.error(
|
|
346
|
+
f"重启消费失败: {str(e)}", exc_info=True)
|
|
347
|
+
# 回滚状态:标记为未消费,避免一直拒绝消息
|
|
348
|
+
await self._set_is_consuming(False)
|
|
338
349
|
except Exception as e:
|
|
339
|
-
logger.error(f"监控任务出错: {str(e)}")
|
|
340
|
-
await asyncio.sleep(1)
|
|
350
|
+
logger.error(f"监控任务出错: {str(e)}", exc_info=True)
|
|
341
351
|
|
|
342
352
|
await asyncio.sleep(60)
|
|
343
353
|
|
|
344
354
|
self._monitor_task = asyncio.create_task(monitor())
|
|
345
355
|
|
|
346
356
|
async def _recreate_channel(self) -> None:
|
|
357
|
+
# 重建前先标记为未消费
|
|
358
|
+
await self._set_is_consuming(False)
|
|
347
359
|
try:
|
|
348
360
|
self.channel = await self._get_channel()
|
|
349
361
|
await self.channel.set_qos(prefetch_count=self.prefetch_count)
|
|
@@ -353,16 +365,22 @@ class RabbitMQClient:
|
|
|
353
365
|
if self.queue_name:
|
|
354
366
|
self.queue = await self.channel.get_queue(self.queue_name)
|
|
355
367
|
if self.queue and self.exchange:
|
|
356
|
-
await self._bind_queue(self.channel, self.queue, self.exchange)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
bound = await self._bind_queue(self.channel, self.queue, self.exchange)
|
|
369
|
+
if not bound:
|
|
370
|
+
raise Exception("队列绑定失败,通道重建不完整")
|
|
371
|
+
|
|
372
|
+
# 重新开始消费(确保状态正确恢复)
|
|
373
|
+
async with self._state_lock:
|
|
374
|
+
has_handler = self.message_handler is not None
|
|
375
|
+
if has_handler:
|
|
360
376
|
await self.start_consuming()
|
|
361
377
|
|
|
362
378
|
logger.info("通道已重建并恢复服务")
|
|
363
379
|
self._update_activity_timestamp()
|
|
364
380
|
except Exception as e:
|
|
365
|
-
logger.error(f"通道重建失败: {str(e)},触发重连")
|
|
381
|
+
logger.error(f"通道重建失败: {str(e)},触发重连", exc_info=True)
|
|
382
|
+
# 重建失败时,确保消费状态为 False
|
|
383
|
+
await self._set_is_consuming(False)
|
|
366
384
|
await self.connect(force_reconnect=True)
|
|
367
385
|
|
|
368
386
|
def _start_keepalive(self) -> None:
|
|
@@ -422,7 +440,7 @@ class RabbitMQClient:
|
|
|
422
440
|
async def close(self) -> None:
|
|
423
441
|
"""关闭客户端并释放资源"""
|
|
424
442
|
self._closed = True
|
|
425
|
-
self._set_is_consuming(False)
|
|
443
|
+
await self._set_is_consuming(False)
|
|
426
444
|
|
|
427
445
|
# 取消所有任务
|
|
428
446
|
for task in [self._keepalive_task, self._reconnect_task,
|
|
@@ -508,16 +526,18 @@ class RabbitMQClient:
|
|
|
508
526
|
|
|
509
527
|
raise Exception(f"消息发布失败,经过{retry_count}次重试仍未成功")
|
|
510
528
|
|
|
511
|
-
def set_message_handler(self, handler):
|
|
512
|
-
self.
|
|
529
|
+
async def set_message_handler(self, handler):
|
|
530
|
+
async with self._state_lock:
|
|
531
|
+
self.message_handler = handler
|
|
513
532
|
|
|
514
533
|
async def start_consuming(self) -> ConsumerTag:
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
534
|
+
async with self._state_lock:
|
|
535
|
+
if self._is_consuming:
|
|
536
|
+
logger.info("已经在消费中,返回现有consumer_tag")
|
|
537
|
+
if self._consumer_tag:
|
|
538
|
+
return self._consumer_tag
|
|
539
|
+
logger.warning("检测到消费状态异常(无consumer_tag),重置状态后重试")
|
|
540
|
+
self._is_consuming = False
|
|
521
541
|
|
|
522
542
|
if not self.is_connected:
|
|
523
543
|
await self.connect()
|
|
@@ -525,8 +545,9 @@ class RabbitMQClient:
|
|
|
525
545
|
if not self.queue:
|
|
526
546
|
raise Exception("队列未初始化,无法开始消费")
|
|
527
547
|
|
|
528
|
-
|
|
529
|
-
|
|
548
|
+
async with self._state_lock:
|
|
549
|
+
if not self.message_handler:
|
|
550
|
+
raise Exception("未设置消息处理函数")
|
|
530
551
|
|
|
531
552
|
try:
|
|
532
553
|
self._consumer_tag = await self.queue.consume(
|
|
@@ -537,12 +558,13 @@ class RabbitMQClient:
|
|
|
537
558
|
if not self._consumer_tag:
|
|
538
559
|
raise Exception("未能获取到有效的consumer_tag")
|
|
539
560
|
|
|
540
|
-
self._set_is_consuming(True)
|
|
561
|
+
await self._set_is_consuming(True)
|
|
541
562
|
logger.info(
|
|
542
563
|
f"消费者已启动,队列: {self.actual_queue_name}, tag: {self._consumer_tag}")
|
|
543
564
|
return self._consumer_tag
|
|
544
565
|
except Exception as e:
|
|
545
|
-
|
|
566
|
+
# 异常时强制设置为未消费
|
|
567
|
+
await self._set_is_consuming(False)
|
|
546
568
|
logger.error(f"启动消费失败: {str(e)}", exc_info=True)
|
|
547
569
|
raise
|
|
548
570
|
|
|
@@ -565,7 +587,7 @@ class RabbitMQClient:
|
|
|
565
587
|
if not self._is_consuming:
|
|
566
588
|
return
|
|
567
589
|
|
|
568
|
-
self._set_is_consuming(False)
|
|
590
|
+
await self._set_is_consuming(False)
|
|
569
591
|
|
|
570
592
|
if self._consumer_tag and self.queue:
|
|
571
593
|
await self._safe_cancel_consumer()
|
|
@@ -602,10 +624,19 @@ class RabbitMQClient:
|
|
|
602
624
|
return message.body.decode('utf-8')
|
|
603
625
|
|
|
604
626
|
async def _message_wrapper(self, message: AbstractIncomingMessage) -> None:
|
|
605
|
-
|
|
606
|
-
|
|
627
|
+
# 先通过锁获取当前状态,避免并发修改导致的判断错误
|
|
628
|
+
async with self._state_lock:
|
|
629
|
+
has_handler = self.message_handler is not None
|
|
630
|
+
is_consuming = self._is_consuming
|
|
631
|
+
|
|
632
|
+
if not has_handler or not is_consuming:
|
|
633
|
+
logger.warning(
|
|
634
|
+
f"拒绝消息: message_handler={'存在' if has_handler else '不存在'}, "
|
|
635
|
+
f"is_consuming={is_consuming},消息ID: {message.message_id or str(id(message))}"
|
|
636
|
+
)
|
|
607
637
|
try:
|
|
608
638
|
await message.reject(requeue=True)
|
|
639
|
+
await asyncio.sleep(3)
|
|
609
640
|
except Exception as e:
|
|
610
641
|
logger.error(f"拒绝消息失败: {e}")
|
|
611
642
|
return
|
|
@@ -35,9 +35,9 @@ sycommon/models/mqlistener_config.py,sha256=vXp2uMmd0XQ5B9noSRXWHewTy-juQ2y7IsWt
|
|
|
35
35
|
sycommon/models/mqmsg_model.py,sha256=cxn0M5b0utQK6crMYmL-1waeGYHvK3AlGaRy23clqTE,277
|
|
36
36
|
sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6D7I,268
|
|
37
37
|
sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
|
|
38
|
-
sycommon/rabbitmq/rabbitmq_client.py,sha256=
|
|
38
|
+
sycommon/rabbitmq/rabbitmq_client.py,sha256=n3c1W31S1NAOkQO2UDpQYgE7Ex8N2hyZrqwPpyxEijU,29733
|
|
39
39
|
sycommon/rabbitmq/rabbitmq_pool.py,sha256=_NMOO4CZy-I_anMqpzfYinz-8373_rg5FM9eqzdjGyU,3598
|
|
40
|
-
sycommon/rabbitmq/rabbitmq_service.py,sha256=
|
|
40
|
+
sycommon/rabbitmq/rabbitmq_service.py,sha256=5BrqF_3PwEMbqs8AVhSqFpqzo3GrIftafMX_tCmFv8E,30199
|
|
41
41
|
sycommon/sse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
sycommon/sse/event.py,sha256=k_rBJy23R7crtzQeetT0Q73D8o5-5p-eESGSs_BPOj0,2797
|
|
43
43
|
sycommon/sse/sse.py,sha256=__CfWEcYxOxQ-HpLor4LTZ5hLWqw9-2X7CngqbVHsfw,10128
|
|
@@ -52,8 +52,8 @@ sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
52
52
|
sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
|
|
53
53
|
sycommon/tools/snowflake.py,sha256=DdEj3T5r5OEvikp3puxqmmmz6BrggxomoSlnsRFb5dM,1174
|
|
54
54
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
55
|
-
sycommon_python_lib-0.1.
|
|
56
|
-
sycommon_python_lib-0.1.
|
|
57
|
-
sycommon_python_lib-0.1.
|
|
58
|
-
sycommon_python_lib-0.1.
|
|
59
|
-
sycommon_python_lib-0.1.
|
|
55
|
+
sycommon_python_lib-0.1.34.dist-info/METADATA,sha256=8m47X4r_A5oq9e1CjQaBkt5gdRTblwW7qyln8kQzC0c,7037
|
|
56
|
+
sycommon_python_lib-0.1.34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
sycommon_python_lib-0.1.34.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
58
|
+
sycommon_python_lib-0.1.34.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
59
|
+
sycommon_python_lib-0.1.34.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.1.33.dist-info → sycommon_python_lib-0.1.34.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|