sycommon-python-lib 0.1.56b5__py3-none-any.whl → 0.1.57b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sycommon/config/Config.py +24 -3
- sycommon/config/LangfuseConfig.py +15 -0
- sycommon/config/SentryConfig.py +13 -0
- sycommon/llm/embedding.py +78 -23
- sycommon/llm/get_llm.py +9 -218
- sycommon/llm/struct_token.py +192 -0
- sycommon/llm/sy_langfuse.py +103 -0
- sycommon/llm/usage_token.py +117 -0
- sycommon/logging/kafka_log.py +187 -433
- sycommon/middleware/exception.py +10 -16
- sycommon/middleware/timeout.py +2 -1
- sycommon/middleware/traceid.py +81 -76
- sycommon/notice/uvicorn_monitor.py +32 -27
- sycommon/rabbitmq/rabbitmq_client.py +228 -243
- sycommon/rabbitmq/rabbitmq_pool.py +201 -123
- sycommon/rabbitmq/rabbitmq_service.py +25 -843
- sycommon/rabbitmq/rabbitmq_service_client_manager.py +211 -0
- sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +73 -0
- sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +285 -0
- sycommon/rabbitmq/rabbitmq_service_core.py +117 -0
- sycommon/rabbitmq/rabbitmq_service_producer_manager.py +238 -0
- sycommon/sentry/__init__.py +0 -0
- sycommon/sentry/sy_sentry.py +35 -0
- sycommon/services.py +122 -96
- sycommon/synacos/nacos_client_base.py +119 -0
- sycommon/synacos/nacos_config_manager.py +107 -0
- sycommon/synacos/nacos_heartbeat_manager.py +144 -0
- sycommon/synacos/nacos_service.py +63 -783
- sycommon/synacos/nacos_service_discovery.py +157 -0
- sycommon/synacos/nacos_service_registration.py +270 -0
- sycommon/tools/env.py +62 -0
- sycommon/tools/merge_headers.py +20 -0
- sycommon/tools/snowflake.py +101 -153
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/METADATA +10 -8
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/RECORD +38 -20
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/top_level.txt +0 -0
|
@@ -19,12 +19,11 @@ class AsyncProperty:
|
|
|
19
19
|
def __get__(self, obj, objtype=None):
|
|
20
20
|
if obj is None:
|
|
21
21
|
return self
|
|
22
|
-
# 关键:当访问 obj.attr 时,直接返回协程对象,而不是方法本身
|
|
23
22
|
return self.method(obj)
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
class RabbitMQConnectionPool:
|
|
27
|
-
"""单连接单通道RabbitMQ客户端 (
|
|
26
|
+
"""单连接单通道RabbitMQ客户端 (严格执行“先清理后连接”策略)"""
|
|
28
27
|
|
|
29
28
|
def __init__(
|
|
30
29
|
self,
|
|
@@ -33,9 +32,9 @@ class RabbitMQConnectionPool:
|
|
|
33
32
|
username: str,
|
|
34
33
|
password: str,
|
|
35
34
|
virtualhost: str = "/",
|
|
36
|
-
heartbeat: int =
|
|
35
|
+
heartbeat: int = 15,
|
|
37
36
|
app_name: str = "",
|
|
38
|
-
connection_timeout: int =
|
|
37
|
+
connection_timeout: int = 15,
|
|
39
38
|
reconnect_interval: int = 5,
|
|
40
39
|
prefetch_count: int = 2,
|
|
41
40
|
):
|
|
@@ -68,124 +67,231 @@ class RabbitMQConnectionPool:
|
|
|
68
67
|
|
|
69
68
|
@AsyncProperty
|
|
70
69
|
async def is_alive(self) -> bool:
|
|
71
|
-
"""
|
|
70
|
+
"""对外暴露的连接存活状态"""
|
|
72
71
|
async with self._lock:
|
|
73
72
|
if self._is_shutdown:
|
|
74
73
|
return False
|
|
75
|
-
|
|
76
74
|
if not self._initialized:
|
|
77
75
|
return False
|
|
78
|
-
|
|
79
76
|
if self._connection is None or self._connection.is_closed:
|
|
80
77
|
return False
|
|
81
|
-
|
|
82
|
-
# 可选:检查主通道是否存活
|
|
83
78
|
if self._channel is None or self._channel.is_closed:
|
|
84
|
-
# 如果你认为通道断了连接也算死,就保留这行;否则删除
|
|
85
79
|
return False
|
|
86
|
-
|
|
87
80
|
return True
|
|
88
81
|
|
|
89
|
-
async def
|
|
82
|
+
async def _cleanup_resources(self):
|
|
90
83
|
"""
|
|
91
|
-
|
|
84
|
+
彻底清理旧资源
|
|
85
|
+
必须在持有 self._lock 的情况下调用
|
|
92
86
|
"""
|
|
87
|
+
logger.info("🧹 [CLEANUP] 开始清理旧资源...")
|
|
88
|
+
|
|
89
|
+
# 1. 清理所有消费者通道
|
|
90
|
+
if self._consumer_channels:
|
|
91
|
+
channels_to_close = list(self._consumer_channels.values())
|
|
92
|
+
self._consumer_channels.clear()
|
|
93
|
+
|
|
94
|
+
for ch in channels_to_close:
|
|
95
|
+
try:
|
|
96
|
+
if not ch.is_closed:
|
|
97
|
+
await ch.close()
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning(f"⚠️ [CLEANUP_CH] 关闭消费者通道失败: {e}")
|
|
100
|
+
|
|
101
|
+
# 2. 关闭主通道
|
|
102
|
+
if self._channel:
|
|
103
|
+
try:
|
|
104
|
+
if not self._channel.is_closed:
|
|
105
|
+
await self._channel.close()
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.warning(f"⚠️ [CLEANUP_MAIN_CH] 关闭主通道失败: {e}")
|
|
108
|
+
finally:
|
|
109
|
+
self._channel = None
|
|
110
|
+
|
|
111
|
+
# 3. 关闭连接
|
|
112
|
+
if self._connection:
|
|
113
|
+
try:
|
|
114
|
+
if not self._connection.is_closed:
|
|
115
|
+
# close() 可能是同步的,也可能是异步的,aio_pika 中通常是异步的
|
|
116
|
+
await self._connection.close()
|
|
117
|
+
except Exception as e:
|
|
118
|
+
logger.warning(f"⚠️ [CLEANUP_CONN] 关闭连接失败: {e}")
|
|
119
|
+
finally:
|
|
120
|
+
self._connection = None
|
|
121
|
+
|
|
122
|
+
logger.info("✅ [CLEANUP] 资源清理完成")
|
|
123
|
+
|
|
124
|
+
async def _create_connection_impl(self, host: str) -> AbstractRobustConnection:
|
|
93
125
|
conn_url = (
|
|
94
|
-
f"amqp://{self.username}:{self.password}@{
|
|
126
|
+
f"amqp://{self.username}:{self.password}@{host}:{self.port}/"
|
|
95
127
|
f"{self.virtualhost}?name={self.app_name}&heartbeat={self.heartbeat}"
|
|
96
128
|
f"&reconnect_interval={self.reconnect_interval}&fail_fast=1"
|
|
97
129
|
)
|
|
98
|
-
logger.info(
|
|
99
|
-
f"🔌 [CONNECT_START] 尝试创建连接 -> {self._current_host}:{self.port}")
|
|
130
|
+
logger.info(f"🔌 [CONNECT] 尝试连接节点: {host}")
|
|
100
131
|
try:
|
|
101
|
-
conn = await
|
|
102
|
-
|
|
103
|
-
|
|
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}")
|
|
104
137
|
return conn
|
|
105
138
|
except Exception as e:
|
|
106
|
-
logger.error(f"❌ [CONNECT_FAIL]
|
|
107
|
-
raise ConnectionError(f"无法连接RabbitMQ {
|
|
108
|
-
|
|
109
|
-
async def _create_channel_impl(self, connection: AbstractRobustConnection) -> RobustChannel:
|
|
110
|
-
"""创建通道"""
|
|
111
|
-
try:
|
|
112
|
-
channel = await connection.channel()
|
|
113
|
-
await channel.set_qos(prefetch_count=self.prefetch_count)
|
|
114
|
-
logger.debug(f"✅ [CHANNEL_OK] 通道创建成功: {id(channel)}")
|
|
115
|
-
return channel
|
|
116
|
-
except Exception as e:
|
|
117
|
-
logger.error(f"❌ [CHANNEL_FAIL] 通道创建失败: {str(e)}")
|
|
118
|
-
raise
|
|
139
|
+
logger.error(f"❌ [CONNECT_FAIL] 节点 {host} 连接失败: {str(e)}")
|
|
140
|
+
raise ConnectionError(f"无法连接RabbitMQ {host}") from e
|
|
119
141
|
|
|
120
142
|
async def _ensure_main_channel(self) -> RobustChannel:
|
|
121
|
-
"""
|
|
143
|
+
"""
|
|
144
|
+
确保主通道有效
|
|
145
|
+
逻辑:
|
|
146
|
+
1. 检查连接状态
|
|
147
|
+
2. 如果断开 -> 清理 -> 轮询重试
|
|
148
|
+
3. 如果连接在但通道断开 -> 仅重建通道
|
|
149
|
+
"""
|
|
122
150
|
async with self._lock:
|
|
123
151
|
if self._is_shutdown:
|
|
124
152
|
raise RuntimeError("客户端已关闭")
|
|
125
153
|
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
if conn is None or conn.is_closed:
|
|
129
|
-
logger.info("⚠️ [RECONNECT] 检测到连接不存在或已关闭,开始重建...")
|
|
130
|
-
conn = await self._create_connection_impl()
|
|
131
|
-
self._connection = conn
|
|
132
|
-
logger.info(f"🔗 [UPDATE] 连接对象已更新: {id(conn)}")
|
|
154
|
+
# --- 阶段A:连接恢复逻辑 (如果连接断了) ---
|
|
155
|
+
if self._connection is None or self._connection.is_closed:
|
|
133
156
|
|
|
134
|
-
|
|
157
|
+
# 1. 【强制】先彻底清理所有旧资源
|
|
158
|
+
await self._cleanup_resources()
|
|
159
|
+
|
|
160
|
+
retry_hosts = self.hosts.copy()
|
|
161
|
+
random.shuffle(retry_hosts)
|
|
162
|
+
last_error = None
|
|
163
|
+
max_attempts = min(len(retry_hosts), 3)
|
|
164
|
+
|
|
165
|
+
# 2. 轮询尝试新连接
|
|
166
|
+
for _ in range(max_attempts):
|
|
167
|
+
if not retry_hosts:
|
|
168
|
+
break
|
|
169
|
+
|
|
170
|
+
host = retry_hosts.pop()
|
|
171
|
+
self._current_host = host
|
|
172
|
+
temp_conn = None
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
temp_conn = await self._create_connection_impl(host)
|
|
176
|
+
|
|
177
|
+
# 3. 只有在连接成功后,才更新 self._connection
|
|
178
|
+
self._connection = temp_conn
|
|
179
|
+
temp_conn = None # 转移所有权
|
|
180
|
+
self._initialized = True
|
|
181
|
+
last_error = None
|
|
182
|
+
logger.info(f"🔗 [RECONNECT_OK] 切换到节点: {host}")
|
|
183
|
+
break
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.warning(f"⚠️ [RECONNECT_RETRY] 节点 {host} 不可用")
|
|
187
|
+
if temp_conn is not None:
|
|
188
|
+
# 尝试连接失败了,必须把这个“半成品”连接关掉
|
|
189
|
+
try:
|
|
190
|
+
await temp_conn.close()
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
last_error = e
|
|
194
|
+
await asyncio.sleep(self.reconnect_interval)
|
|
195
|
+
|
|
196
|
+
# 4. 如果所有尝试都失败
|
|
197
|
+
if last_error:
|
|
198
|
+
# 确保状态是干净的
|
|
199
|
+
self._connection = None
|
|
200
|
+
self._initialized = False
|
|
201
|
+
logger.error("💥 [RECONNECT_FATAL] 所有节点重试失败")
|
|
202
|
+
raise ConnectionError("所有 RabbitMQ 节点连接失败") from last_error
|
|
203
|
+
|
|
204
|
+
# --- 阶段B:通道恢复逻辑 (如果连接在但通道断了) ---
|
|
205
|
+
# 注意:这里不需要清理连接,只重置通道
|
|
135
206
|
if self._channel is None or self._channel.is_closed:
|
|
136
|
-
|
|
137
|
-
|
|
207
|
+
try:
|
|
208
|
+
self._channel = await self._connection.channel()
|
|
209
|
+
await self._channel.set_qos(prefetch_count=self.prefetch_count)
|
|
210
|
+
logger.info(f"✅ [CHANNEL_OK] 主通道已恢复")
|
|
211
|
+
except Exception as e:
|
|
212
|
+
# 如果连通道都创建不了,说明这个连接也是坏的,回滚到阶段A
|
|
213
|
+
logger.error(f"❌ [CHANNEL_FAIL] 通道创建失败,标记连接无效: {e}")
|
|
214
|
+
# 强制清理连接,触发下一次进入阶段A
|
|
215
|
+
await self._cleanup_resources()
|
|
216
|
+
raise
|
|
138
217
|
|
|
139
218
|
return self._channel
|
|
140
219
|
|
|
141
220
|
async def init_pools(self):
|
|
142
|
-
"""
|
|
143
|
-
初始化入口与异常处理 (修复泄漏的关键)
|
|
144
|
-
"""
|
|
221
|
+
"""初始化入口"""
|
|
145
222
|
async with self._lock:
|
|
146
223
|
if self._is_shutdown:
|
|
147
224
|
raise RuntimeError("客户端已关闭")
|
|
148
225
|
if self._initialized:
|
|
149
226
|
return
|
|
150
227
|
|
|
228
|
+
# 在 try 之前声明变量,确保 except 块能访问
|
|
151
229
|
conn_created_in_this_try = None
|
|
230
|
+
|
|
152
231
|
try:
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
|
|
232
|
+
# 锁外创建连接,减少锁持有时间
|
|
233
|
+
init_host = random.choice(self.hosts)
|
|
234
|
+
conn = await self._create_connection_impl(init_host)
|
|
235
|
+
|
|
236
|
+
# 记录本次创建的连接
|
|
237
|
+
conn_created_in_this_try = conn
|
|
156
238
|
|
|
157
|
-
# 步骤 B: 更新状态和初始化通道 (在锁内进行,保证原子性)
|
|
158
239
|
async with self._lock:
|
|
159
240
|
if self._is_shutdown:
|
|
160
|
-
# 如果在创建连接期间,外部调用了 close,则必须立即清理刚创建的连接
|
|
161
|
-
logger.warning("⚠️ [ABORT] 检测到关闭信号,放弃初始化并清理资源")
|
|
162
241
|
raise RuntimeError("客户端已关闭")
|
|
163
242
|
|
|
243
|
+
# 提交新资源
|
|
164
244
|
self._connection = conn
|
|
165
|
-
self._channel = await self.
|
|
245
|
+
self._channel = await self._connection.channel()
|
|
246
|
+
await self._channel.set_qos(prefetch_count=self.prefetch_count)
|
|
166
247
|
self._initialized = True
|
|
167
|
-
|
|
168
|
-
|
|
248
|
+
|
|
249
|
+
# 所有权转移成功,清空临时引用,防止 finally 重复关闭
|
|
250
|
+
conn_created_in_this_try = None
|
|
251
|
+
|
|
252
|
+
logger.info(f"🚀 [INIT_OK] 连接池初始化完成: {init_host}")
|
|
169
253
|
|
|
170
254
|
except Exception as e:
|
|
171
|
-
logger.error(f"💥 [
|
|
172
|
-
|
|
173
|
-
#
|
|
174
|
-
if conn_created_in_this_try:
|
|
175
|
-
logger.warning(
|
|
176
|
-
f"🧹 [LEAK_PREVENTION] 检测到初始化失败,正在显式关闭刚创建的连接: {id(conn_created_in_this_try)}")
|
|
255
|
+
logger.error(f"💥 [INIT_FAIL] 初始化异常: {str(e)}")
|
|
256
|
+
|
|
257
|
+
# 这里现在可以合法访问 conn_created_in_this_try
|
|
258
|
+
if conn_created_in_this_try is not None:
|
|
177
259
|
try:
|
|
178
260
|
await conn_created_in_this_try.close()
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
except Exception as close_err:
|
|
182
|
-
logger.error(f"❌ [CLOSE_ERR] 关闭泄漏连接时出错: {str(close_err)}")
|
|
261
|
+
except Exception:
|
|
262
|
+
pass
|
|
183
263
|
|
|
184
|
-
# 如果是因为中途关闭导致的错误,不需要再次调用全局 close,否则调用
|
|
185
264
|
if not self._is_shutdown:
|
|
186
265
|
await self.close()
|
|
187
266
|
raise
|
|
188
267
|
|
|
268
|
+
async def force_reconnect(self):
|
|
269
|
+
"""
|
|
270
|
+
强制重连
|
|
271
|
+
严格执行:清理所有资源 -> 尝试建立新资源
|
|
272
|
+
"""
|
|
273
|
+
async with self._lock:
|
|
274
|
+
if self._is_shutdown:
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
logger.warning("🔄 [FORCE_RECONNECT] 开始强制重连...")
|
|
278
|
+
|
|
279
|
+
# 1. 【关键】标记未初始化,迫使 _ensure_main_channel 走清理流程
|
|
280
|
+
self._initialized = False
|
|
281
|
+
|
|
282
|
+
# 2. 【关键】立即清理旧资源 (在锁内)
|
|
283
|
+
await self._cleanup_resources()
|
|
284
|
+
|
|
285
|
+
# 此时 self._connection 和 self._channel 均为 None
|
|
286
|
+
|
|
287
|
+
# 3. 锁外触发恢复 (避免阻塞锁太久)
|
|
288
|
+
try:
|
|
289
|
+
await self.acquire_channel()
|
|
290
|
+
logger.info("✅ [FORCE_RECONNECT_OK] 强制重连成功")
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error(f"❌ [FORCE_RECONNECT_FAIL] 强制重连失败: {e}")
|
|
293
|
+
raise
|
|
294
|
+
|
|
189
295
|
async def acquire_channel(self) -> Tuple[RobustChannel, AbstractRobustConnection]:
|
|
190
296
|
"""获取主通道"""
|
|
191
297
|
if not self._initialized and not self._is_shutdown:
|
|
@@ -193,22 +299,20 @@ class RabbitMQConnectionPool:
|
|
|
193
299
|
return await self._ensure_main_channel(), self._connection
|
|
194
300
|
|
|
195
301
|
async def publish_message(self, routing_key: str, message_body: bytes, exchange_name: str = "", **kwargs):
|
|
196
|
-
"""发布消息"""
|
|
197
302
|
channel, _ = await self.acquire_channel()
|
|
198
303
|
try:
|
|
199
304
|
exchange = channel.default_exchange if not exchange_name else await channel.get_exchange(exchange_name)
|
|
200
305
|
message = Message(body=message_body, **kwargs)
|
|
201
306
|
await exchange.publish(message, routing_key=routing_key)
|
|
202
|
-
logger.debug(f"📤 [PUBLISH] 消息发布成功 - RK: {routing_key}")
|
|
203
307
|
except Exception as e:
|
|
204
308
|
logger.error(f"❌ [PUBLISH_FAIL] 发布失败: {str(e)}")
|
|
205
309
|
raise
|
|
206
310
|
|
|
207
311
|
async def consume_queue(self, queue_name: str, callback: Callable[[AbstractMessage], asyncio.Future], auto_ack: bool = False, **kwargs):
|
|
208
|
-
"""消费队列"""
|
|
209
312
|
if not self._initialized:
|
|
210
313
|
await self.init_pools()
|
|
211
314
|
|
|
315
|
+
# 检查是否已存在
|
|
212
316
|
async with self._lock:
|
|
213
317
|
if self._is_shutdown:
|
|
214
318
|
raise RuntimeError("客户端已关闭")
|
|
@@ -218,20 +322,26 @@ class RabbitMQConnectionPool:
|
|
|
218
322
|
if not self._connection or self._connection.is_closed:
|
|
219
323
|
raise RuntimeError("连接不可用,无法启动消费")
|
|
220
324
|
|
|
325
|
+
# 声明队列 (使用主通道)
|
|
221
326
|
await self.declare_queue(queue_name, **kwargs)
|
|
222
327
|
|
|
223
328
|
try:
|
|
224
|
-
#
|
|
225
|
-
conn = self.
|
|
329
|
+
# 获取最新连接
|
|
330
|
+
_, conn = await self.acquire_channel()
|
|
331
|
+
|
|
332
|
+
# 创建消费者通道
|
|
226
333
|
consumer_channel = await conn.channel()
|
|
227
334
|
await consumer_channel.set_qos(prefetch_count=self.prefetch_count)
|
|
228
|
-
logger.info(
|
|
229
|
-
f"✅ [CONSUMER_CHANNEL_OK] 消费者通道创建: {id(consumer_channel)}")
|
|
230
335
|
|
|
231
336
|
async with self._lock:
|
|
337
|
+
# 再次检查,防止并发创建
|
|
232
338
|
if self._is_shutdown:
|
|
233
339
|
await consumer_channel.close()
|
|
234
340
|
return
|
|
341
|
+
if queue_name in self._consumer_channels:
|
|
342
|
+
await consumer_channel.close() # 其他协程已经创建了
|
|
343
|
+
return
|
|
344
|
+
|
|
235
345
|
self._consumer_channels[queue_name] = consumer_channel
|
|
236
346
|
|
|
237
347
|
async def consume_callback_wrapper(message: AbstractMessage):
|
|
@@ -240,77 +350,45 @@ class RabbitMQConnectionPool:
|
|
|
240
350
|
if not auto_ack:
|
|
241
351
|
await message.ack()
|
|
242
352
|
except Exception as e:
|
|
243
|
-
logger.error(
|
|
244
|
-
f"❌ [CALLBACK_ERR] 消费回调异常 {queue_name}: {str(e)}")
|
|
353
|
+
logger.error(f"❌ [CALLBACK_ERR] {queue_name}: {e}")
|
|
245
354
|
if not auto_ack:
|
|
246
355
|
await message.nack(requeue=True)
|
|
247
356
|
|
|
248
357
|
await consumer_channel.basic_consume(
|
|
249
|
-
queue_name, consumer_callback=consume_callback_wrapper, auto_ack=auto_ack
|
|
358
|
+
queue_name, consumer_callback=consume_callback_wrapper, auto_ack=auto_ack
|
|
250
359
|
)
|
|
251
|
-
logger.info(f"🎧 [CONSUME_START]
|
|
360
|
+
logger.info(f"🎧 [CONSUME_START] {queue_name}")
|
|
252
361
|
|
|
253
362
|
except Exception as e:
|
|
254
|
-
logger.error(f"💥 [CONSUME_ERR]
|
|
363
|
+
logger.error(f"💥 [CONSUME_ERR] {queue_name}: {e}")
|
|
364
|
+
# 失败时清理字典
|
|
255
365
|
async with self._lock:
|
|
256
366
|
if queue_name in self._consumer_channels:
|
|
257
|
-
|
|
367
|
+
# 注意:这里清理的是字典里的引用,通道本身应该在 try 块里被关闭了吗?
|
|
368
|
+
# 如果 consumer_channel 创建成功但 basic_consume 失败,需要手动关闭
|
|
369
|
+
ch = self._consumer_channels.pop(queue_name, None)
|
|
370
|
+
if ch:
|
|
371
|
+
try:
|
|
372
|
+
await ch.close()
|
|
373
|
+
except:
|
|
374
|
+
pass
|
|
258
375
|
raise
|
|
259
376
|
|
|
260
377
|
async def close(self):
|
|
261
|
-
"""
|
|
262
|
-
资源销毁入口
|
|
263
|
-
"""
|
|
378
|
+
"""资源销毁"""
|
|
264
379
|
async with self._lock:
|
|
265
380
|
if self._is_shutdown:
|
|
266
381
|
return
|
|
267
382
|
self._is_shutdown = True
|
|
268
383
|
self._initialized = False
|
|
269
|
-
# 记录即将关闭的连接ID
|
|
270
|
-
conn_to_close_id = id(
|
|
271
|
-
self._connection) if self._connection else None
|
|
272
|
-
|
|
273
|
-
logger.info(f"🛑 [CLOSE_START] 开始关闭客户端... (准备关闭连接: {conn_to_close_id})")
|
|
274
|
-
|
|
275
|
-
# 1. 关闭消费者通道
|
|
276
|
-
channels_to_close = []
|
|
277
|
-
async with self._lock:
|
|
278
|
-
channels_to_close = list(self._consumer_channels.values())
|
|
279
|
-
self._consumer_channels.clear()
|
|
280
|
-
|
|
281
|
-
for ch in channels_to_close:
|
|
282
|
-
try:
|
|
283
|
-
if not ch.is_closed:
|
|
284
|
-
await ch.close()
|
|
285
|
-
logger.debug(f"✅ [CLOSE_CHANNEL] 消费者通道已关闭")
|
|
286
|
-
except Exception as e:
|
|
287
|
-
logger.warning(f"❌ [CLOSE_CHANNEL_ERR] 关闭消费者通道失败: {str(e)}")
|
|
288
384
|
|
|
289
|
-
|
|
290
|
-
if self._channel:
|
|
291
|
-
try:
|
|
292
|
-
if not self._channel.is_closed:
|
|
293
|
-
await self._channel.close()
|
|
294
|
-
logger.info(f"✅ [CLOSE_CHANNEL] 主通道已关闭")
|
|
295
|
-
except Exception:
|
|
296
|
-
pass
|
|
297
|
-
self._channel = None
|
|
385
|
+
logger.info("🛑 [CLOSE] 开始关闭连接池...")
|
|
298
386
|
|
|
299
|
-
#
|
|
300
|
-
|
|
301
|
-
if self._connection:
|
|
302
|
-
try:
|
|
303
|
-
# 打印关闭操作
|
|
304
|
-
logger.info(f"🔌 [CLOSE_CONN] 正在关闭连接: {id(self._connection)}")
|
|
305
|
-
await self._connection.close()
|
|
306
|
-
logger.info(f"✅ [CLOSE_OK] 连接已成功关闭: {id(self._connection)}")
|
|
307
|
-
except Exception as e:
|
|
308
|
-
logger.warning(f"❌ [CLOSE_ERR] 关闭连接失败: {str(e)}")
|
|
309
|
-
self._connection = None
|
|
387
|
+
# 1. 清理所有资源
|
|
388
|
+
await self._cleanup_resources()
|
|
310
389
|
|
|
311
|
-
logger.info("🏁 [
|
|
390
|
+
logger.info("🏁 [CLOSE] 连接池已关闭")
|
|
312
391
|
|
|
313
|
-
# --- 辅助方法省略 (declare_queue 等) ---
|
|
314
392
|
async def declare_queue(self, queue_name: str, **kwargs) -> AbstractQueue:
|
|
315
393
|
channel, _ = await self.acquire_channel()
|
|
316
394
|
return await channel.declare_queue(queue_name, **kwargs)
|