sycommon-python-lib 0.2.0b2__tar.gz → 0.2.0b4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/PKG-INFO +1 -1
  2. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/pyproject.toml +1 -1
  3. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_client.py +149 -105
  4. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_pool.py +46 -81
  5. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +7 -17
  6. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +1 -1
  7. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +2 -2
  8. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +8 -8
  9. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
  10. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/README.md +0 -0
  11. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/setup.cfg +0 -0
  12. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/command/cli.py +0 -0
  13. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/__init__.py +0 -0
  14. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/Config.py +0 -0
  15. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/DatabaseConfig.py +0 -0
  16. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/EmbeddingConfig.py +0 -0
  17. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/LLMConfig.py +0 -0
  18. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/LangfuseConfig.py +0 -0
  19. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/MQConfig.py +0 -0
  20. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/RerankerConfig.py +0 -0
  21. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/SentryConfig.py +0 -0
  22. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/config/__init__.py +0 -0
  23. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/database/async_base_db_service.py +0 -0
  24. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/database/async_database_service.py +0 -0
  25. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/database/base_db_service.py +0 -0
  26. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/database/database_service.py +0 -0
  27. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/health/__init__.py +0 -0
  28. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/health/health_check.py +0 -0
  29. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/health/metrics.py +0 -0
  30. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/health/ping.py +0 -0
  31. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/__init__.py +0 -0
  32. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/embedding.py +0 -0
  33. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/get_llm.py +0 -0
  34. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/llm_logger.py +0 -0
  35. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/llm_tokens.py +0 -0
  36. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/struct_token.py +0 -0
  37. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/sy_langfuse.py +0 -0
  38. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/llm/usage_token.py +0 -0
  39. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/__init__.py +0 -0
  40. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/async_sql_logger.py +0 -0
  41. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/kafka_log.py +0 -0
  42. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/logger_levels.py +0 -0
  43. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/logger_wrapper.py +0 -0
  44. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/logging/sql_logger.py +0 -0
  45. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/__init__.py +0 -0
  46. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/context.py +0 -0
  47. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/cors.py +0 -0
  48. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/docs.py +0 -0
  49. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/exception.py +0 -0
  50. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/middleware.py +0 -0
  51. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/monitor_memory.py +0 -0
  52. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/mq.py +0 -0
  53. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/timeout.py +0 -0
  54. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/middleware/traceid.py +0 -0
  55. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/__init__.py +0 -0
  56. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/base_http.py +0 -0
  57. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/log.py +0 -0
  58. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/mqlistener_config.py +0 -0
  59. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/mqmsg_model.py +0 -0
  60. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/mqsend_config.py +0 -0
  61. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/models/sso_user.py +0 -0
  62. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/notice/__init__.py +0 -0
  63. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  64. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  65. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  66. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/sentry/__init__.py +0 -0
  67. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/sentry/sy_sentry.py +0 -0
  68. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/services.py +0 -0
  69. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/sse/__init__.py +0 -0
  70. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/sse/event.py +0 -0
  71. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/sse/sse.py +0 -0
  72. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/__init__.py +0 -0
  73. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/example.py +0 -0
  74. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/example2.py +0 -0
  75. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/feign.py +0 -0
  76. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/feign_client.py +0 -0
  77. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_client_base.py +0 -0
  78. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  79. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  80. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_service.py +0 -0
  81. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  82. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  83. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/synacos/param.py +0 -0
  84. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tests/test_email.py +0 -0
  85. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/__init__.py +0 -0
  86. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/docs.py +0 -0
  87. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/env.py +0 -0
  88. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/merge_headers.py +0 -0
  89. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/snowflake.py +0 -0
  90. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/syemail.py +0 -0
  91. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon/tools/timing.py +0 -0
  92. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
  93. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  94. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  95. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
  96. {sycommon_python_lib-0.2.0b2 → sycommon_python_lib-0.2.0b4}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.0b2
3
+ Version: 0.2.0b4
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.2.0b2"
3
+ version = "0.2.0b4"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import functools
2
3
  import json
3
4
  from typing import Optional, Callable, Coroutine, Dict, Any, Union
4
5
  from aio_pika import Channel, Message, DeliveryMode, ExchangeType
@@ -67,6 +68,7 @@ class RabbitMQClient:
67
68
  # 并发控制
68
69
  self._consume_lock = asyncio.Lock()
69
70
  self._connect_lock = asyncio.Lock()
71
+ self._reconnect_lock = asyncio.Lock()
70
72
 
71
73
  # 防止并发重连覆盖
72
74
  self._connecting = False
@@ -78,34 +80,46 @@ class RabbitMQClient:
78
80
  self._RECONNECT_INTERVAL = 15
79
81
 
80
82
  @property
81
- async def is_connected(self) -> bool:
83
+ def is_connected(self) -> bool:
84
+ """
85
+ 同步检查连接状态
86
+ 【修复】改为同步属性,避免异步调用错误和布尔上下文误判
87
+ """
82
88
  if self._closed:
83
89
  return False
84
90
  try:
91
+ # 检查通道是否有效
92
+ if not self._channel or self._channel.is_closed:
93
+ return False
94
+
95
+ # 检查连接是否有效
96
+ if self._channel_conn and self._channel_conn.is_closed:
97
+ return False
98
+
85
99
  return (
86
- self._channel and not self._channel.is_closed
87
- and self._channel_conn and not self._channel_conn.is_closed
88
- and self._exchange is not None
100
+ self._exchange is not None
89
101
  and (not self.queue_name or self._queue is not None)
90
102
  )
91
103
  except Exception:
92
104
  return False
93
105
 
94
106
  async def _rebuild_resources(self) -> None:
107
+ """重建 Exchange 和 Queue 资源"""
95
108
  if not self._channel or self._channel.is_closed:
96
109
  raise RuntimeError("无有效通道,无法重建资源")
97
110
 
98
- # 声明交换机
99
- self._exchange = await self._channel.declare_exchange(
111
+ # 1. 无条件声明交换机
112
+ exchange_inst = await self._channel.declare_exchange(
100
113
  name=self.exchange_name,
101
114
  type=self.exchange_type,
102
115
  durable=self.durable,
103
116
  auto_delete=self.auto_delete,
104
117
  passive=not self.create_if_not_exists,
105
118
  )
119
+ self._exchange = exchange_inst
106
120
  logger.info(f"交换机重建成功: {self.exchange_name}")
107
121
 
108
- # 声明队列
122
+ # 2. 仅在有队列名且符合条件时声明队列
109
123
  if self.queue_name and self.queue_name.endswith(f".{self.app_name}"):
110
124
  self._queue = await self._channel.declare_queue(
111
125
  name=self.queue_name,
@@ -113,61 +127,76 @@ class RabbitMQClient:
113
127
  auto_delete=self.auto_delete,
114
128
  passive=not self.create_if_not_exists,
115
129
  )
116
- await self._queue.bind(exchange=self._exchange, routing_key=self.routing_key)
130
+ await self._queue.bind(exchange=exchange_inst, routing_key=self.routing_key)
117
131
  logger.info(f"队列重建成功: {self.queue_name}")
118
132
 
133
+ async def _ensure_connection_alive(self) -> AbstractRobustConnection:
134
+ """
135
+ 【新增安全辅助方法】
136
+ 动态获取当前有效的连接对象,避免引用池中已过期的连接。
137
+ 如果连接无效,会抛出异常,交由上层逻辑处理重连。
138
+ """
139
+ if not self.connection_pool or not self.connection_pool._initialized:
140
+ raise RuntimeError("连接池未初始化")
141
+
142
+ conn = self.connection_pool._connection
143
+ if not conn or conn.is_closed:
144
+ raise RuntimeError("连接池底层连接已关闭")
145
+ return conn
146
+
119
147
  async def connect(self) -> None:
120
- """连接方法(消费者独立通道 + 移除无效属性检查 + 强制重建队列)"""
148
+ """连接方法(终极防御版:防并发风暴、防死锁、防资源泄漏)"""
121
149
  if self._closed:
122
150
  raise RuntimeError("客户端已关闭,无法重新连接")
123
151
 
124
- # 1. 获取 Condition 锁,用于管理连接并发和等待
125
- await self._connect_condition.acquire()
126
-
127
- try:
128
- # ===== 阶段 A: 检查状态与排队 =====
129
- if await self.is_connected:
130
- if self._connect_condition.locked():
131
- self._connect_condition.release()
132
- return
133
-
134
- # 如果已有协程正在连接,等待其完成
135
- if self._connecting:
136
- try:
137
- logger.debug("连接正在进行中,等待现有连接完成...")
138
- await asyncio.wait_for(self._connect_condition.wait(), timeout=60.0)
139
- except asyncio.TimeoutError:
140
- logger.warning("等待前序连接超时,当前协程将尝试强制接管并重连...")
141
-
142
- # 唤醒后再次检查状态,防止重复连接
143
- if await self.is_connected:
152
+ # === 阶段 A: 并发控制循环 ===
153
+ while True:
154
+ await self._connect_condition.acquire()
155
+ try:
156
+ # 1. 检查是否已连接
157
+ if self.is_connected:
144
158
  if self._connect_condition.locked():
145
159
  self._connect_condition.release()
146
160
  return
147
161
 
148
- # ===== 阶段 B: 标记开始连接并释放锁 =====
149
- # 释放锁是为了让耗时的连接过程不阻塞其他协程
150
- self._connecting = True
151
- self._connect_condition.release()
162
+ # 2. 检查是否已有协程在连接
163
+ if self._connecting:
164
+ try:
165
+ logger.debug("连接正在进行中,等待现有连接完成...")
166
+ # 等待其他协程完成
167
+ await asyncio.wait_for(self._connect_condition.wait(), timeout=60.0)
168
+ except asyncio.TimeoutError:
169
+ logger.warning("⚠️ 等待前序连接超时,重新竞争连接权...")
170
+
171
+ # 【核心修复】
172
+ # 无论是因为超时还是被唤醒,都 continue 重新循环
173
+ # 这样确保不会出现“所有协程同时醒来”的羊群效应
174
+ # 只有抢到锁的那个协程会进入连接逻辑,其他继续 wait
175
+ if self._connect_condition.locked():
176
+ self._connect_condition.release()
177
+ continue
152
178
 
153
- except Exception as e:
154
- if self._connect_condition.locked():
179
+ # 3. 抢到连接权,标记开始
180
+ self._connecting = True
155
181
  self._connect_condition.release()
156
- raise
182
+ break # <--- 跳出循环,去执行连接逻辑
183
+
184
+ except Exception as e:
185
+ if self._connect_condition.locked():
186
+ self._connect_condition.release()
187
+ raise
157
188
 
158
189
  # === 阶段 C: 执行耗时的连接逻辑 (无锁状态) ===
159
190
  connection_failed = False
160
191
  was_consuming = False
161
-
162
- # 判断当前是否为消费者模式(通过是否有消息处理函数判断)
163
192
  is_consumer = self._message_handler is not None
164
193
  old_channel = self._channel
165
194
 
166
195
  try:
167
- # --- 步骤 1: 记录状态并清理旧资源 ---
196
+ # --- 步骤 1: 清理旧资源 ---
168
197
  was_consuming = self._consumer_tag is not None
169
198
 
170
- # 清理旧连接的 close_callbacks,防止重连触发多次
199
+ # 清理旧连接的回调
171
200
  if self._channel_conn:
172
201
  try:
173
202
  if self._channel_conn.close_callbacks:
@@ -175,56 +204,57 @@ class RabbitMQClient:
175
204
  except Exception:
176
205
  pass
177
206
 
178
- # 显式关闭旧 Channel
179
- # 注意:无论是生产者复用的主通道,还是消费者的独立通道,断开时都应显式关闭以释放服务端资源
180
207
  if old_channel and not old_channel.is_closed:
181
208
  try:
182
209
  await old_channel.close()
183
210
  except Exception:
184
211
  pass
185
212
 
186
- # 【修复点】强制重置所有核心资源引用
187
- # 因为我们即将获取一个新的 Channel,旧的 Exchange 和 Queue 对象(基于旧 Channel)将全部失效。
188
- # 必须置为 None,强制后续逻辑基于新 Channel 重建这些对象。
213
+ # 强制重置
189
214
  self._channel = None
190
215
  self._channel_conn = None
191
216
  self._exchange = None
192
217
  self._queue = None
193
218
  self._consumer_tag = None
194
219
 
195
- # --- 步骤 2: 根据角色获取新连接 ---
196
- # 生产者:复用连接池的主通道(性能高)
197
- # 消费者:从连接池获取独立的通道(稳定性高,避免并发冲突)
220
+ # --- 步骤 2: 获取新通道 ---
198
221
  if is_consumer:
199
222
  logger.debug("获取消费者独立通道...")
200
223
  self._channel = await self.connection_pool.acquire_consumer_channel()
201
- self._channel_conn = self.connection_pool._connection
202
224
  else:
203
225
  logger.debug("获取生产者主通道...")
204
226
  self._channel, self._channel_conn = await self.connection_pool.acquire_channel()
205
227
 
206
- # --- 步骤 3: 设置连接关闭回调 ---
228
+ # --- 步骤 3: 统一获取并注册连接回调 ---
229
+ if not self._channel_conn:
230
+ # 消费者路径需要手动获取连接引用
231
+ # 直接引用连接池的连接对象,确保引用时效性
232
+ self._channel_conn = self.connection_pool._connection
233
+
207
234
  loop = asyncio.get_running_loop()
208
235
 
209
236
  def on_conn_closed(conn, exc):
210
- if self._closed:
211
- return
212
- logger.warning(f"检测到底层连接关闭: {exc}")
213
- # 确保在循环中安全调用协程
214
- asyncio.run_coroutine_threadsafe(self._safe_reconnect(), loop)
237
+ # 【核心修复】不再判断 _connecting,直接委托给 _safe_reconnect
238
+ # _safe_reconnect 内部有锁,会自动处理并发问题
239
+ if not self._closed:
240
+ logger.warning(f"⚠️ 检测到底层连接关闭: {exc}")
241
+ asyncio.run_coroutine_threadsafe(
242
+ self._safe_reconnect(), loop)
243
+
244
+ # 注册回调前,再次防御性清理
245
+ if self._channel_conn.close_callbacks:
246
+ self._channel_conn.close_callbacks.clear()
215
247
 
216
- if self._channel_conn:
217
- self._channel_conn.close_callbacks.add(on_conn_closed)
248
+ self._channel_conn.close_callbacks.add(on_conn_closed)
218
249
 
219
250
  # --- 步骤 4: 重建基础资源 ---
220
- # 这会在新的 self._channel 上声明 Exchange 和 Queue,并执行绑定
221
251
  await self._rebuild_resources()
222
252
 
223
253
  except Exception as e:
224
254
  connection_failed = True
225
- logger.error(f"客户端连接失败: {str(e)}", exc_info=True)
255
+ logger.error(f"客户端连接失败: {str(e)}", exc_info=True)
226
256
 
227
- # 发生异常时清理引用
257
+ # 异常时清理
228
258
  if self._channel_conn and self._channel_conn.close_callbacks:
229
259
  self._channel_conn.close_callbacks.clear()
230
260
 
@@ -233,31 +263,25 @@ class RabbitMQClient:
233
263
  self._exchange = None
234
264
  self._queue = None
235
265
  self._consumer_tag = None
236
-
237
266
  raise
238
267
 
239
268
  finally:
240
- # === 阶段 D: 恢复消费与收尾 (重新加锁) ===
269
+ # === 阶段 D: 恢复消费与收尾 ===
241
270
  try:
242
271
  await self._connect_condition.acquire()
243
272
  except Exception:
244
- pass
273
+ # 如果 acquire 本身失败,确保状态复位,防止死锁
274
+ self._connecting = False
275
+ self._connect_condition.notify_all()
276
+ raise
245
277
 
246
278
  try:
247
- # 只有连接完全成功,且之前处于消费状态,才尝试自动恢复消费
248
279
  if not connection_failed and was_consuming and self._message_handler:
249
280
  logger.info("🔄 检测到重连前处于消费状态,尝试自动恢复消费...")
250
281
 
251
- # 【修复核心】
252
- # 由于在步骤 1 中 self._queue 已被置为 None,
253
- # 如果 _rebuild_resources 因为某种原因(例如配置条件)没有成功创建队列,
254
- # 这里需要再次尝试在当前新 Channel 上创建并绑定队列。
255
- # 不再检查 is_closed(因为该属性不存在),直接检查是否为 None。
256
-
257
282
  if self.queue_name and not self._queue:
258
283
  try:
259
284
  logger.info(f"重连恢复过程中重新声明队列: {self.queue_name}")
260
- # 在当前新 Channel 上声明队列
261
285
  self._queue = await self._channel.declare_queue(
262
286
  name=self.queue_name,
263
287
  durable=self.durable,
@@ -265,18 +289,25 @@ class RabbitMQClient:
265
289
  passive=not self.create_if_not_exists,
266
290
  )
267
291
 
268
- # 【关键步骤】显式绑定队列
269
- # 即使队列已存在,也必须在新 Channel 上重新绑定,否则服务端路由状态可能不更新
292
+ # 确保绑定
270
293
  if self._exchange:
271
294
  await self._queue.bind(exchange=self._exchange, routing_key=self.routing_key)
272
295
  logger.info(
273
296
  f"✅ 重连绑定成功: {self.queue_name} -> {self.routing_key}")
297
+ else:
298
+ # 防御性编程
299
+ logger.error(
300
+ "🔥 Exchange missing, forcing declare...")
301
+ temp_ex = await self._channel.declare_exchange(
302
+ self.exchange_name, self.exchange_type, durable=self.durable
303
+ )
304
+ self._exchange = temp_ex
305
+ await self._queue.bind(exchange=self._exchange, routing_key=self.routing_key)
306
+
274
307
  except Exception as bind_err:
275
308
  logger.error(f"❌ 重连恢复队列/绑定失败: {bind_err}")
276
- # 绑定失败,无法恢复消费
277
309
  self._queue = None
278
310
 
279
- # 队列对象有效才启动消费
280
311
  if self._queue:
281
312
  try:
282
313
  self._consumer_tag = await self.start_consuming()
@@ -284,41 +315,35 @@ class RabbitMQClient:
284
315
  except Exception as e:
285
316
  logger.error(f"❌ 自动恢复消费失败: {e}")
286
317
  self._consumer_tag = None
287
- else:
288
- logger.warning("⚠️ 队列对象无效,无法恢复消费")
289
-
290
318
  finally:
291
- # 最终状态复位
319
+ # 无论恢复逻辑成功与否,必须解锁
292
320
  self._connecting = False
293
321
  self._connect_condition.notify_all()
294
-
295
322
  if self._connect_condition.locked():
296
323
  self._connect_condition.release()
297
324
 
298
325
  async def _safe_reconnect(self):
299
- """安全重连任务(仅用于被动监听连接关闭)"""
300
- async with self._reconnect_semaphore:
301
- if self._closed:
302
- return
303
-
304
- # 如果已经在重连,直接忽略
305
- if self._connecting:
306
- return
307
-
308
- logger.info(f"将在{self._RECONNECT_INTERVAL}秒后尝试重连...")
309
- await asyncio.sleep(self._RECONNECT_INTERVAL)
310
-
311
- if self._closed or await self.is_connected:
312
- return
326
+ """
327
+ 安全重连入口
328
+ 【核心修复】使用锁防止并发风暴,确保同一时间只有一个重连任务在执行。
329
+ """
330
+ # 如果锁已经被占用,说明已经有重连任务在进行中,直接忽略,防止无限递归
331
+ if self._reconnect_lock.locked():
332
+ logger.debug("⏳ 重连任务已在执行中,忽略本次触发,避免并发风暴")
333
+ return
313
334
 
335
+ async with self._reconnect_lock:
314
336
  try:
315
- self._current_reconnect_task = asyncio.create_task(
316
- self.connect())
317
- await self._current_reconnect_task
337
+ if self._closed:
338
+ logger.info("客户端已手动关闭,取消重连")
339
+ return
340
+
341
+ logger.warning("🔄 触发底层连接重连...")
342
+ # 调用实际的 connect 方法
343
+ await self.connect()
318
344
  except Exception as e:
319
- logger.warning(f"重连失败: {str(e)}")
320
- finally:
321
- self._current_reconnect_task = None
345
+ # 即使重连失败,也不要在这里递归调用自己,而是依赖外部的定时任务或下一次网络事件
346
+ logger.error(f"❌ 安全重连执行失败: {str(e)}", exc_info=True)
322
347
 
323
348
  async def set_message_handler(self, handler: Callable[..., Coroutine]) -> None:
324
349
  if not asyncio.iscoroutinefunction(handler):
@@ -327,23 +352,42 @@ class RabbitMQClient:
327
352
  self._message_handler = handler
328
353
 
329
354
  async def _process_message_callback(self, message: AbstractIncomingMessage):
355
+ """
356
+ 消息处理回调
357
+ """
330
358
  try:
359
+ # === 阶段 1: 消息解析与上下文设置 (在主线程执行) ===
331
360
  body_dict = json.loads(message.body.decode("utf-8"))
332
361
  msg_obj: MQMsgModel = MQMsgModel(**body_dict)
362
+
333
363
  if not msg_obj.traceId:
334
364
  msg_obj.traceId = message.headers.get(
335
365
  "trace-id") if message.headers else SYLogger.get_trace_id()
336
366
 
337
367
  SYLogger.set_trace_id(msg_obj.traceId)
338
368
 
369
+ # === 阶段 2: 业务逻辑执行 ===
339
370
  if self._message_handler:
340
- await self._message_handler(msg_obj, message)
371
+ def run_job_with_context():
372
+ # 【关键修复】在子线程内重新设置 TraceId
373
+ SYLogger.set_trace_id(msg_obj.traceId)
341
374
 
375
+ return asyncio.run(
376
+ functools.partial(
377
+ self._message_handler, msg_obj, message)()
378
+ )
379
+
380
+ await asyncio.to_thread(run_job_with_context)
381
+
382
+ # === 阶段 3: 消息确认 ===
342
383
  await message.ack()
343
384
 
344
385
  except Exception as e:
345
- logger.error(f"消息处理异常: {e}", exc_info=True)
346
- await message.ack()
386
+ logger.error(f"消息处理异常: {str(e)}", exc_info=True)
387
+ try:
388
+ await message.ack()
389
+ except Exception:
390
+ pass
347
391
 
348
392
  async def start_consuming(self) -> Optional[ConsumerTag]:
349
393
  if self._closed:
@@ -353,7 +397,7 @@ class RabbitMQClient:
353
397
  if not self._message_handler:
354
398
  raise RuntimeError("未设置消息处理器")
355
399
 
356
- if not await self.is_connected:
400
+ if not self.is_connected:
357
401
  await self.connect()
358
402
 
359
403
  if not self._queue:
@@ -431,7 +475,7 @@ class RabbitMQClient:
431
475
 
432
476
  for retry in range(retry_count):
433
477
  try:
434
- if not await self.is_connected:
478
+ if not self.is_connected:
435
479
  await self.connect()
436
480
 
437
481
  result = await self._exchange.publish(
@@ -23,7 +23,7 @@ class AsyncProperty:
23
23
 
24
24
 
25
25
  class RabbitMQConnectionPool:
26
- """单连接单通道RabbitMQ客户端 (严格执行“先清理后连接”策略)"""
26
+ """单连接单通道RabbitMQ客户端"""
27
27
 
28
28
  def __init__(
29
29
  self,
@@ -73,6 +73,7 @@ class RabbitMQConnectionPool:
73
73
  return False
74
74
  if not self._initialized:
75
75
  return False
76
+ # 修复:简化检查,移除耗时的 connect() 调用,直接检查状态
76
77
  if self._connection is None or self._connection.is_closed:
77
78
  return False
78
79
  if self._channel is None or self._channel.is_closed:
@@ -112,7 +113,6 @@ class RabbitMQConnectionPool:
112
113
  if self._connection:
113
114
  try:
114
115
  if not self._connection.is_closed:
115
- # close() 可能是同步的,也可能是异步的,aio_pika 中通常是异步的
116
116
  await self._connection.close()
117
117
  except Exception as e:
118
118
  logger.warning(f"⚠️ [CLEANUP_CONN] 关闭连接失败: {e}")
@@ -121,67 +121,29 @@ class RabbitMQConnectionPool:
121
121
 
122
122
  logger.info("✅ [CLEANUP] 资源清理完成")
123
123
 
124
- async def _create_connection_impl(self, host: str) -> AbstractRobustConnection:
125
- conn_url = (
126
- f"amqp://{self.username}:{self.password}@{host}:{self.port}/"
127
- f"{self.virtualhost}?name={self.app_name}&heartbeat={self.heartbeat}"
128
- f"&reconnect_interval={self.reconnect_interval}&fail_fast=1"
129
- )
130
- logger.info(f"🔌 [CONNECT] 尝试连接节点: {host}")
131
- try:
132
- conn = await asyncio.wait_for(
133
- connect_robust(conn_url),
134
- timeout=self.connection_timeout + 5
135
- )
136
- logger.info(f"✅ [CONNECT_OK] 节点连接成功: {host}")
137
- return conn
138
- except Exception as e:
139
- logger.error(f"❌ [CONNECT_FAIL] 节点 {host} 连接失败: {str(e)}")
140
- raise ConnectionError(f"无法连接RabbitMQ {host}") from e
141
-
142
124
  async def _ensure_main_channel(self) -> RobustChannel:
143
125
  """
144
- 确保主通道有效 (修复:将探活逻辑移入锁内,防止死锁)
126
+ 确保主通道有效 (修复版:移除会导致死锁的代码)
145
127
  """
146
128
  async with self._lock: # 持有锁贯穿整个方法
147
129
  if self._is_shutdown:
148
130
  raise RuntimeError("客户端已关闭")
149
131
 
150
132
  # --- 阶段 A: 连接检查与重建 ---
133
+ # 修复:直接检查 is_closed,不调用 connect(timeout=1) 防止阻塞
151
134
  connection_is_dead = False
152
- if self._connection is None:
135
+ if self._connection is None or self._connection.is_closed:
153
136
  connection_is_dead = True
154
- else:
155
- try:
156
- if self._connection.is_closed:
157
- connection_is_dead = True
158
- else:
159
- # 【修复】显式探活,保持在锁内
160
- # 这样如果探活失败,可以直接在锁内进入重建流程,无需重新竞争锁
161
- try:
162
- await asyncio.wait_for(self._connection.connect(timeout=1), timeout=15)
163
- except Exception:
164
- logger.warning("⚠️ 连接探活失败,判定为死连接,强制重建...")
165
- connection_is_dead = True
166
- except Exception:
167
- connection_is_dead = True
168
137
 
169
- # 如果连接死掉,执行清理和重建
170
138
  if connection_is_dead:
171
139
  await self._cleanup_resources()
172
- # ... (重建连接逻辑保持不变: 遍历 hosts -> connect_robust -> 赋值 self._connection) ...
173
- # 确保重建成功,否则抛出异常
174
140
 
175
- # 为了代码完整性,这里补全重建逻辑的核心部分(基于你之前的代码)
141
+ # 重建连接逻辑
176
142
  retry_hosts = self.hosts.copy()
177
143
  random.shuffle(retry_hosts)
178
144
  last_error = None
179
- max_attempts = min(len(retry_hosts), 3)
180
145
 
181
- for _ in range(max_attempts):
182
- if not retry_hosts:
183
- break
184
- host = retry_hosts.pop()
146
+ for host in retry_hosts:
185
147
  self._current_host = host
186
148
  temp_conn = None
187
149
  try:
@@ -192,8 +154,13 @@ class RabbitMQConnectionPool:
192
154
  )
193
155
  temp_conn = await asyncio.wait_for(
194
156
  connect_robust(conn_url),
195
- timeout=self.connection_timeout + 5
157
+ timeout=self.connection_timeout
196
158
  )
159
+
160
+ if self._is_shutdown:
161
+ await temp_conn.close()
162
+ raise RuntimeError("客户端已关闭")
163
+
197
164
  self._connection = temp_conn
198
165
  self._initialized = True
199
166
  last_error = None
@@ -211,10 +178,11 @@ class RabbitMQConnectionPool:
211
178
  await asyncio.sleep(self.reconnect_interval)
212
179
 
213
180
  if last_error:
214
- raise ConnectionError("所有 RabbitMQ 节点连接失败") from last_error
181
+ self._initialized = False
182
+ raise ConnectionError(
183
+ f"所有 RabbitMQ 节点连接失败: {last_error}") from last_error
215
184
 
216
185
  # --- 阶段 B: 通道恢复逻辑 ---
217
- # 此时 self._connection 必须是有效的
218
186
  if self._channel is None or self._channel.is_closed:
219
187
  max_channel_attempts = 2
220
188
  for attempt in range(max_channel_attempts):
@@ -227,18 +195,19 @@ class RabbitMQConnectionPool:
227
195
  logger.warning(
228
196
  f"⚠️ [CHANNEL_RETRY] 第 {attempt + 1} 次尝试创建通道失败: {e}")
229
197
  if attempt < max_channel_attempts - 1:
230
- await self._cleanup_resources() # 通道失败导致连接可能也坏了,重置
231
- # 简单重试逻辑:这里抛出异常让外层重试,或者在这里递归调用
232
- # 鉴于复杂度,建议抛出异常
233
- raise e
198
+ try:
199
+ await self._connection.close()
200
+ except:
201
+ pass
202
+ self._connection = None
234
203
  else:
235
- raise e
204
+ raise RuntimeError(f"创建通道失败: {e}")
236
205
  else:
237
- # 通道存在,进行一次轻量级探活 (保持一致性)
206
+ # 通道存在,进行一次轻量级 QOS 刷新
238
207
  try:
239
208
  await self._channel.set_qos(prefetch_count=self.prefetch_count)
240
209
  except Exception:
241
- logger.warning("⚠️ 通道探活失败,重建通道...")
210
+ logger.warning("⚠️ 通道 QOS 刷新失败,重建通道...")
242
211
  self._channel = await self._connection.channel()
243
212
  await self._channel.set_qos(prefetch_count=self.prefetch_count)
244
213
 
@@ -246,14 +215,12 @@ class RabbitMQConnectionPool:
246
215
 
247
216
  async def init_pools(self):
248
217
  """初始化入口"""
249
- # 快速检查
250
218
  if self._initialized:
251
219
  return
252
220
 
253
221
  conn_created_in_this_try = None
254
222
 
255
223
  try:
256
- # 锁外创建连接,减少锁持有时间
257
224
  init_host = random.choice(self.hosts)
258
225
  conn = await self._create_connection_impl(init_host)
259
226
  conn_created_in_this_try = conn
@@ -262,14 +229,12 @@ class RabbitMQConnectionPool:
262
229
  if self._is_shutdown:
263
230
  raise RuntimeError("客户端已关闭")
264
231
 
265
- # 双重检查:防止在锁外等待时,状态已改变或被其他协程初始化
266
232
  if self._initialized:
267
233
  logger.info("🚀 [INIT_SKIP] 其他协程已完成初始化")
268
234
  if conn_created_in_this_try:
269
235
  await conn_created_in_this_try.close()
270
236
  return
271
237
 
272
- # 提交新资源
273
238
  self._connection = conn
274
239
  self._channel = await self._connection.channel()
275
240
  await self._channel.set_qos(prefetch_count=self.prefetch_count)
@@ -286,29 +251,38 @@ class RabbitMQConnectionPool:
286
251
  except Exception:
287
252
  pass
288
253
  if not self._is_shutdown:
289
- await self.close()
254
+ async with self._lock:
255
+ self._initialized = False
290
256
  raise
291
257
 
258
+ async def _create_connection_impl(self, host: str) -> AbstractRobustConnection:
259
+ conn_url = (
260
+ f"amqp://{self.username}:{self.password}@{host}:{self.port}/"
261
+ f"{self.virtualhost}?name={self.app_name}&heartbeat={self.heartbeat}"
262
+ f"&reconnect_interval={self.reconnect_interval}&fail_fast=1"
263
+ )
264
+ logger.info(f"🔌 [CONNECT] 尝试连接节点: {host}")
265
+ try:
266
+ conn = await asyncio.wait_for(
267
+ connect_robust(conn_url),
268
+ timeout=self.connection_timeout + 5
269
+ )
270
+ logger.info(f"✅ [CONNECT_OK] 节点连接成功: {host}")
271
+ return conn
272
+ except Exception as e:
273
+ logger.error(f"❌ [CONNECT_FAIL] 节点 {host} 连接失败: {str(e)}")
274
+ raise ConnectionError(f"无法连接RabbitMQ {host}") from e
275
+
292
276
  async def force_reconnect(self):
293
- """
294
- 强制重连
295
- 严格执行:清理所有资源 -> 尝试建立新资源
296
- """
277
+ """强制重连"""
297
278
  async with self._lock:
298
279
  if self._is_shutdown:
299
280
  return
300
281
 
301
282
  logger.warning("🔄 [FORCE_RECONNECT] 开始强制重连...")
302
-
303
- # 1. 【关键】标记未初始化,迫使 _ensure_main_channel 走清理流程
304
283
  self._initialized = False
305
-
306
- # 2. 【关键】立即清理旧资源 (在锁内)
307
284
  await self._cleanup_resources()
308
285
 
309
- # 此时 self._connection 和 self._channel 均为 None
310
-
311
- # 3. 锁外触发恢复 (避免阻塞锁太久)
312
286
  try:
313
287
  await self.acquire_channel()
314
288
  logger.info("✅ [FORCE_RECONNECT_OK] 强制重连成功")
@@ -317,20 +291,13 @@ class RabbitMQConnectionPool:
317
291
  raise
318
292
 
319
293
  async def acquire_consumer_channel(self) -> RobustChannel:
320
- """
321
- 专门为消费者获取独立的通道。
322
- 遵循 aio_pika 最佳实践:消费者不应与发布者或其他消费者共享同一个 Channel 对象。
323
- """
324
- # 确保连接池已初始化且连接是活的
294
+ """获取消费者独立通道"""
325
295
  if not self._initialized:
326
296
  await self.init_pools()
327
297
 
328
- # 确保 self._connection 是有效的(复用 _ensure_main_channel 的连接恢复逻辑)
329
298
  await self._ensure_main_channel()
330
299
 
331
- # 基于有效连接创建一个新的独立通道
332
300
  try:
333
- # 注意:这里直接使用 self._connection,而不是返回缓存的 self._channel
334
301
  consumer_ch = await self._connection.channel()
335
302
  await consumer_ch.set_qos(prefetch_count=self.prefetch_count)
336
303
  logger.debug("✅ [CONSUMER_CH] 消费者独立通道已创建")
@@ -424,11 +391,9 @@ class RabbitMQConnectionPool:
424
391
  async def close(self):
425
392
  """资源销毁"""
426
393
  try:
427
- # 设置超时,防止因锁异常导致无法关闭
428
394
  await asyncio.wait_for(self._lock.acquire(), timeout=5.0)
429
395
  except asyncio.TimeoutError:
430
396
  logger.error("⚠️ [CLOSE_TIMEOUT] 获取锁超时,强制标记关闭")
431
- # 如果拿不到锁,我们只能标记状态,无法安全清理连接
432
397
  self._is_shutdown = True
433
398
  self._initialized = False
434
399
  return
@@ -29,29 +29,17 @@ class RabbitMQClientManager(RabbitMQCoreService):
29
29
  @classmethod
30
30
  async def _clean_client_resources(cls, client: RabbitMQClient) -> None:
31
31
  """
32
- 清理客户端无效资源(修正版:只清理消费行为,不关闭 Channel)
33
-
34
- 重要变更:
35
- - 移除了 await client._channel.close() 调用。
36
- - 原因:关闭 Channel 可能会触发底层连接的 close_callback,导致重连死锁或冲突。
37
- - Channel 的关闭应该由 RabbitMQClient.connect() 内部的重建逻辑接管。
32
+ 清理客户端无效资源
38
33
  """
39
34
  try:
40
- # 1. 尝试正常停止消费(发送 basic_cancel)
35
+ # 1. 停止消费
41
36
  if client._consumer_tag:
42
37
  await client.stop_consuming()
43
38
  except Exception as e:
44
39
  logger.warning(f"停止消费逻辑异常: {str(e)}")
45
40
 
46
- # 注意:这里不再显式关闭 client._channel
47
- # 原因:
48
- # 1. 如果是消费者独立通道,关闭它是安全的,但不如交给 connect() 统一处理(重建前关闭)。
49
- # 2. 如果是生产者主通道(由 Pool 缓存),绝对不能在这里关闭!否则会影响其他生产者。
50
- # 3. connect() 方法已经包含了 "显式关闭旧 Channel" 的逻辑,这里重复操作是多余的且危险的。
51
-
52
41
  try:
53
- # 2. 重置客户端状态引用(为后续 connect() 扫清障碍)
54
- # 注意:虽然清理了状态,但不要清空 _message_handler!
42
+ # 2. 重置引用
55
43
  client._channel = None
56
44
  client._channel_conn = None
57
45
  client._exchange = None
@@ -59,6 +47,8 @@ class RabbitMQClientManager(RabbitMQCoreService):
59
47
  client._consumer_tag = None
60
48
  except Exception as e:
61
49
  logger.warning(f"重置客户端状态异常: {str(e)}")
50
+ # 注意:不要在这里关闭 _channel,交给 client.close() 或者重连逻辑处理
51
+ # 这里主要确保引用断开,帮助 GC
62
52
 
63
53
  @classmethod
64
54
  async def _reconnect_client(cls, client_name: str, client: RabbitMQClient) -> bool:
@@ -95,7 +85,7 @@ class RabbitMQClientManager(RabbitMQCoreService):
95
85
  await client.connect()
96
86
 
97
87
  # 验证重连结果
98
- if await client.is_connected:
88
+ if client.is_connected:
99
89
  logger.info(f"✅ 客户端 '{client_name}' 重连成功")
100
90
  return True
101
91
  else:
@@ -209,7 +199,7 @@ class RabbitMQClientManager(RabbitMQCoreService):
209
199
  if not client.queue_name and kwargs.get("queue_name"):
210
200
  client.queue_name = kwargs.get("queue_name")
211
201
 
212
- if await client.is_connected:
202
+ if client.is_connected:
213
203
  return client
214
204
  else:
215
205
  logger.info(f"客户端 '{client_name}' 连接已断开,重新创建")
@@ -33,7 +33,7 @@ class RabbitMQConnectionMonitor(RabbitMQClientManager):
33
33
  # 检查所有客户端连接
34
34
  for client_name, client in list(cls._clients.items()):
35
35
  try:
36
- client_connected = await client.is_connected
36
+ client_connected = client.is_connected
37
37
  if not client_connected:
38
38
  logger.warning(
39
39
  f"客户端 '{client_name}' 连接异常,触发重连")
@@ -160,7 +160,7 @@ class RabbitMQConsumerManager(RabbitMQClientManager):
160
160
 
161
161
  # 确保客户端已连接
162
162
  start_time = asyncio.get_event_loop().time()
163
- while not await client.is_connected and not cls._is_shutdown:
163
+ while not client.is_connected and not cls._is_shutdown:
164
164
  if asyncio.get_event_loop().time() - start_time > cls.CONSUMER_START_TIMEOUT:
165
165
  raise TimeoutError(f"等待客户端 '{client_name}' 连接超时")
166
166
 
@@ -184,7 +184,7 @@ class RabbitMQConsumerManager(RabbitMQClientManager):
184
184
  while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
185
185
  try:
186
186
  # 启动消费前再次校验
187
- if not await client.is_connected:
187
+ if not client.is_connected:
188
188
  logger.info(f"消费者 '{client_name}' 连接断开,尝试重连")
189
189
  await client.connect()
190
190
 
@@ -56,7 +56,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
56
56
  # ===== 处理已有客户端重连 =====
57
57
  if normalized_name in cls._clients:
58
58
  client = cls._clients[normalized_name]
59
- if not await client.is_connected:
59
+ if not client.is_connected:
60
60
  client.queue_name = normalized_name
61
61
  client.create_if_not_exists = False
62
62
  await client.connect()
@@ -104,14 +104,14 @@ class RabbitMQProducerManager(RabbitMQClientManager):
104
104
  # 检查是否在已注册的发送器中
105
105
  if queue_name in cls._sender_client_names and queue_name in cls._clients:
106
106
  client = cls._clients[queue_name]
107
- if await client.is_connected:
107
+ if client.is_connected:
108
108
  return client
109
109
  else:
110
110
  logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
111
111
  try:
112
112
  client.create_if_not_exists = False
113
113
  await client.connect()
114
- if await client.is_connected:
114
+ if client.is_connected:
115
115
  return client
116
116
  except Exception as e:
117
117
  logger.error(f"发送器 '{queue_name}' 重连失败: {str(e)}")
@@ -123,14 +123,14 @@ class RabbitMQProducerManager(RabbitMQClientManager):
123
123
  suffixed_name = f"{queue_name}.{app_name}"
124
124
  if suffixed_name in cls._sender_client_names and suffixed_name in cls._clients:
125
125
  client = cls._clients[suffixed_name]
126
- if await client.is_connected:
126
+ if client.is_connected:
127
127
  return client
128
128
  else:
129
129
  logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
130
130
  try:
131
131
  client.create_if_not_exists = False
132
132
  await client.connect()
133
- if await client.is_connected:
133
+ if client.is_connected:
134
134
  return client
135
135
  except Exception as e:
136
136
  logger.error(f"发送器 '{suffixed_name}' 重连失败: {str(e)}")
@@ -156,7 +156,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
156
156
  raise ValueError(error_msg)
157
157
 
158
158
  # 确保连接有效
159
- if not await sender.is_connected:
159
+ if not sender.is_connected:
160
160
  logger.info(f"发送器 '{queue_name}' 连接已关闭,尝试重新连接")
161
161
  max_retry = 3
162
162
  retry_count = 0
@@ -166,7 +166,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
166
166
  try:
167
167
  sender.create_if_not_exists = False
168
168
  await sender.connect()
169
- if await sender.is_connected:
169
+ if sender.is_connected:
170
170
  logger.info(
171
171
  f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
172
172
  break
@@ -177,7 +177,7 @@ class RabbitMQProducerManager(RabbitMQClientManager):
177
177
  f"发送器 '{queue_name}' 第 {retry_count} 次重连失败: {str(e)}")
178
178
  await asyncio.sleep(cls.RECONNECT_INTERVAL)
179
179
 
180
- if retry_count >= max_retry and not await sender.is_connected:
180
+ if retry_count >= max_retry and not sender.is_connected:
181
181
  error_msg = f"发送器 '{queue_name}' 经过 {max_retry} 次重连仍失败"
182
182
  logger.error(f"{error_msg}: {str(last_exception)}")
183
183
  raise Exception(error_msg) from last_exception
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.0b2
3
+ Version: 0.2.0b4
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown