sycommon-python-lib 0.1.56b11__py3-none-any.whl → 0.1.56b13__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/llm/embedding.py +78 -23
- sycommon/rabbitmq/rabbitmq_client.py +224 -290
- sycommon/rabbitmq/rabbitmq_pool.py +182 -154
- sycommon/rabbitmq/rabbitmq_service_core.py +2 -2
- {sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/RECORD +9 -9
- {sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.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,174 +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
|
-
#
|
|
154
|
+
# --- 阶段A:连接恢复逻辑 (如果连接断了) ---
|
|
127
155
|
if self._connection is None or self._connection.is_closed:
|
|
128
|
-
|
|
156
|
+
|
|
157
|
+
# 1. 【强制】先彻底清理所有旧资源
|
|
158
|
+
await self._cleanup_resources()
|
|
159
|
+
|
|
160
|
+
retry_hosts = self.hosts.copy()
|
|
161
|
+
random.shuffle(retry_hosts)
|
|
129
162
|
last_error = None
|
|
130
|
-
|
|
163
|
+
max_attempts = min(len(retry_hosts), 3)
|
|
131
164
|
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
165
|
+
# 2. 轮询尝试新连接
|
|
166
|
+
for _ in range(max_attempts):
|
|
167
|
+
if not retry_hosts:
|
|
168
|
+
break
|
|
136
169
|
|
|
137
|
-
|
|
170
|
+
host = retry_hosts.pop()
|
|
138
171
|
self._current_host = host
|
|
139
|
-
temp_conn = None
|
|
172
|
+
temp_conn = None
|
|
140
173
|
|
|
141
174
|
try:
|
|
142
|
-
|
|
143
|
-
f"amqp://{self.username}:{self.password}@{host}:{self.port}/"
|
|
144
|
-
f"{self.virtualhost}?name={self.app_name}&heartbeat={self.heartbeat}"
|
|
145
|
-
f"&reconnect_interval={self.reconnect_interval}&fail_fast=1"
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
# 尝试连接
|
|
149
|
-
temp_conn = await connect_robust(conn_url, timeout=self.connection_timeout)
|
|
175
|
+
temp_conn = await self._create_connection_impl(host)
|
|
150
176
|
|
|
151
|
-
#
|
|
177
|
+
# 3. 只有在连接成功后,才更新 self._connection
|
|
152
178
|
self._connection = temp_conn
|
|
153
|
-
temp_conn = None #
|
|
154
|
-
|
|
155
|
-
logger.info(f"🔗 [RECONNECT_OK] 成功连接到节点: {host}")
|
|
179
|
+
temp_conn = None # 转移所有权
|
|
180
|
+
self._initialized = True
|
|
156
181
|
last_error = None
|
|
157
|
-
|
|
182
|
+
logger.info(f"🔗 [RECONNECT_OK] 切换到节点: {host}")
|
|
183
|
+
break
|
|
158
184
|
|
|
159
185
|
except Exception as e:
|
|
160
|
-
logger.warning(
|
|
161
|
-
f"❌ [RECONNECT_FAIL] 节点 {host} 不可用: {str(e)}")
|
|
162
|
-
|
|
163
|
-
# 【核心修复】清理失败的连接对象
|
|
186
|
+
logger.warning(f"⚠️ [RECONNECT_RETRY] 节点 {host} 不可用")
|
|
164
187
|
if temp_conn is not None:
|
|
188
|
+
# 尝试连接失败了,必须把这个“半成品”连接关掉
|
|
165
189
|
try:
|
|
166
|
-
logger.debug(
|
|
167
|
-
f"🧹 [CLEANUP] 正在关闭失败的连接对象: {id(temp_conn)}")
|
|
168
|
-
# 即使连接对象处于异常状态,close() 通常也是安全的
|
|
169
190
|
await temp_conn.close()
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
logger.warning(
|
|
173
|
-
f"⚠️ [CLEANUP_ERR] 关闭失败连接时出错: {str(close_err)}")
|
|
174
|
-
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
175
193
|
last_error = e
|
|
176
|
-
asyncio.sleep(self.reconnect_interval)
|
|
177
|
-
continue # 继续试下一个
|
|
194
|
+
await asyncio.sleep(self.reconnect_interval)
|
|
178
195
|
|
|
179
|
-
#
|
|
196
|
+
# 4. 如果所有尝试都失败
|
|
180
197
|
if last_error:
|
|
181
|
-
|
|
198
|
+
# 确保状态是干净的
|
|
199
|
+
self._connection = None
|
|
200
|
+
self._initialized = False
|
|
201
|
+
logger.error("💥 [RECONNECT_FATAL] 所有节点重试失败")
|
|
182
202
|
raise ConnectionError("所有 RabbitMQ 节点连接失败") from last_error
|
|
183
203
|
|
|
184
|
-
#
|
|
204
|
+
# --- 阶段B:通道恢复逻辑 (如果连接在但通道断了) ---
|
|
205
|
+
# 注意:这里不需要清理连接,只重置通道
|
|
185
206
|
if self._channel is None or self._channel.is_closed:
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
188
217
|
|
|
189
218
|
return self._channel
|
|
190
219
|
|
|
191
220
|
async def init_pools(self):
|
|
192
|
-
"""
|
|
193
|
-
初始化入口与异常处理 (修复泄漏的关键)
|
|
194
|
-
"""
|
|
221
|
+
"""初始化入口"""
|
|
195
222
|
async with self._lock:
|
|
196
223
|
if self._is_shutdown:
|
|
197
224
|
raise RuntimeError("客户端已关闭")
|
|
198
225
|
if self._initialized:
|
|
199
226
|
return
|
|
200
227
|
|
|
228
|
+
# 在 try 之前声明变量,确保 except 块能访问
|
|
201
229
|
conn_created_in_this_try = None
|
|
230
|
+
|
|
202
231
|
try:
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
206
238
|
|
|
207
|
-
# 步骤 B: 更新状态和初始化通道 (在锁内进行,保证原子性)
|
|
208
239
|
async with self._lock:
|
|
209
240
|
if self._is_shutdown:
|
|
210
|
-
# 如果在创建连接期间,外部调用了 close,则必须立即清理刚创建的连接
|
|
211
|
-
logger.warning("⚠️ [ABORT] 检测到关闭信号,放弃初始化并清理资源")
|
|
212
241
|
raise RuntimeError("客户端已关闭")
|
|
213
242
|
|
|
243
|
+
# 提交新资源
|
|
214
244
|
self._connection = conn
|
|
215
|
-
self._channel = await self.
|
|
245
|
+
self._channel = await self._connection.channel()
|
|
246
|
+
await self._channel.set_qos(prefetch_count=self.prefetch_count)
|
|
216
247
|
self._initialized = True
|
|
217
|
-
|
|
218
|
-
|
|
248
|
+
|
|
249
|
+
# 所有权转移成功,清空临时引用,防止 finally 重复关闭
|
|
250
|
+
conn_created_in_this_try = None
|
|
251
|
+
|
|
252
|
+
logger.info(f"🚀 [INIT_OK] 连接池初始化完成: {init_host}")
|
|
219
253
|
|
|
220
254
|
except Exception as e:
|
|
221
|
-
logger.error(f"💥 [
|
|
222
|
-
|
|
223
|
-
#
|
|
224
|
-
if conn_created_in_this_try:
|
|
225
|
-
logger.warning(
|
|
226
|
-
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:
|
|
227
259
|
try:
|
|
228
260
|
await conn_created_in_this_try.close()
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
except Exception as close_err:
|
|
232
|
-
logger.error(f"❌ [CLOSE_ERR] 关闭泄漏连接时出错: {str(close_err)}")
|
|
261
|
+
except Exception:
|
|
262
|
+
pass
|
|
233
263
|
|
|
234
|
-
# 如果是因为中途关闭导致的错误,不需要再次调用全局 close,否则调用
|
|
235
264
|
if not self._is_shutdown:
|
|
236
265
|
await self.close()
|
|
237
266
|
raise
|
|
238
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
|
+
|
|
239
295
|
async def acquire_channel(self) -> Tuple[RobustChannel, AbstractRobustConnection]:
|
|
240
296
|
"""获取主通道"""
|
|
241
297
|
if not self._initialized and not self._is_shutdown:
|
|
@@ -243,22 +299,20 @@ class RabbitMQConnectionPool:
|
|
|
243
299
|
return await self._ensure_main_channel(), self._connection
|
|
244
300
|
|
|
245
301
|
async def publish_message(self, routing_key: str, message_body: bytes, exchange_name: str = "", **kwargs):
|
|
246
|
-
"""发布消息"""
|
|
247
302
|
channel, _ = await self.acquire_channel()
|
|
248
303
|
try:
|
|
249
304
|
exchange = channel.default_exchange if not exchange_name else await channel.get_exchange(exchange_name)
|
|
250
305
|
message = Message(body=message_body, **kwargs)
|
|
251
306
|
await exchange.publish(message, routing_key=routing_key)
|
|
252
|
-
logger.debug(f"📤 [PUBLISH] 消息发布成功 - RK: {routing_key}")
|
|
253
307
|
except Exception as e:
|
|
254
308
|
logger.error(f"❌ [PUBLISH_FAIL] 发布失败: {str(e)}")
|
|
255
309
|
raise
|
|
256
310
|
|
|
257
311
|
async def consume_queue(self, queue_name: str, callback: Callable[[AbstractMessage], asyncio.Future], auto_ack: bool = False, **kwargs):
|
|
258
|
-
"""消费队列"""
|
|
259
312
|
if not self._initialized:
|
|
260
313
|
await self.init_pools()
|
|
261
314
|
|
|
315
|
+
# 检查是否已存在
|
|
262
316
|
async with self._lock:
|
|
263
317
|
if self._is_shutdown:
|
|
264
318
|
raise RuntimeError("客户端已关闭")
|
|
@@ -268,20 +322,26 @@ class RabbitMQConnectionPool:
|
|
|
268
322
|
if not self._connection or self._connection.is_closed:
|
|
269
323
|
raise RuntimeError("连接不可用,无法启动消费")
|
|
270
324
|
|
|
325
|
+
# 声明队列 (使用主通道)
|
|
271
326
|
await self.declare_queue(queue_name, **kwargs)
|
|
272
327
|
|
|
273
328
|
try:
|
|
274
|
-
#
|
|
275
|
-
conn = self.
|
|
329
|
+
# 获取最新连接
|
|
330
|
+
_, conn = await self.acquire_channel()
|
|
331
|
+
|
|
332
|
+
# 创建消费者通道
|
|
276
333
|
consumer_channel = await conn.channel()
|
|
277
334
|
await consumer_channel.set_qos(prefetch_count=self.prefetch_count)
|
|
278
|
-
logger.info(
|
|
279
|
-
f"✅ [CONSUMER_CHANNEL_OK] 消费者通道创建: {id(consumer_channel)}")
|
|
280
335
|
|
|
281
336
|
async with self._lock:
|
|
337
|
+
# 再次检查,防止并发创建
|
|
282
338
|
if self._is_shutdown:
|
|
283
339
|
await consumer_channel.close()
|
|
284
340
|
return
|
|
341
|
+
if queue_name in self._consumer_channels:
|
|
342
|
+
await consumer_channel.close() # 其他协程已经创建了
|
|
343
|
+
return
|
|
344
|
+
|
|
285
345
|
self._consumer_channels[queue_name] = consumer_channel
|
|
286
346
|
|
|
287
347
|
async def consume_callback_wrapper(message: AbstractMessage):
|
|
@@ -290,77 +350,45 @@ class RabbitMQConnectionPool:
|
|
|
290
350
|
if not auto_ack:
|
|
291
351
|
await message.ack()
|
|
292
352
|
except Exception as e:
|
|
293
|
-
logger.error(
|
|
294
|
-
f"❌ [CALLBACK_ERR] 消费回调异常 {queue_name}: {str(e)}")
|
|
353
|
+
logger.error(f"❌ [CALLBACK_ERR] {queue_name}: {e}")
|
|
295
354
|
if not auto_ack:
|
|
296
355
|
await message.nack(requeue=True)
|
|
297
356
|
|
|
298
357
|
await consumer_channel.basic_consume(
|
|
299
|
-
queue_name, consumer_callback=consume_callback_wrapper, auto_ack=auto_ack
|
|
358
|
+
queue_name, consumer_callback=consume_callback_wrapper, auto_ack=auto_ack
|
|
300
359
|
)
|
|
301
|
-
logger.info(f"🎧 [CONSUME_START]
|
|
360
|
+
logger.info(f"🎧 [CONSUME_START] {queue_name}")
|
|
302
361
|
|
|
303
362
|
except Exception as e:
|
|
304
|
-
logger.error(f"💥 [CONSUME_ERR]
|
|
363
|
+
logger.error(f"💥 [CONSUME_ERR] {queue_name}: {e}")
|
|
364
|
+
# 失败时清理字典
|
|
305
365
|
async with self._lock:
|
|
306
366
|
if queue_name in self._consumer_channels:
|
|
307
|
-
|
|
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
|
|
308
375
|
raise
|
|
309
376
|
|
|
310
377
|
async def close(self):
|
|
311
|
-
"""
|
|
312
|
-
资源销毁入口
|
|
313
|
-
"""
|
|
378
|
+
"""资源销毁"""
|
|
314
379
|
async with self._lock:
|
|
315
380
|
if self._is_shutdown:
|
|
316
381
|
return
|
|
317
382
|
self._is_shutdown = True
|
|
318
383
|
self._initialized = False
|
|
319
|
-
# 记录即将关闭的连接ID
|
|
320
|
-
conn_to_close_id = id(
|
|
321
|
-
self._connection) if self._connection else None
|
|
322
|
-
|
|
323
|
-
logger.info(f"🛑 [CLOSE_START] 开始关闭客户端... (准备关闭连接: {conn_to_close_id})")
|
|
324
384
|
|
|
325
|
-
|
|
326
|
-
channels_to_close = []
|
|
327
|
-
async with self._lock:
|
|
328
|
-
channels_to_close = list(self._consumer_channels.values())
|
|
329
|
-
self._consumer_channels.clear()
|
|
330
|
-
|
|
331
|
-
for ch in channels_to_close:
|
|
332
|
-
try:
|
|
333
|
-
if not ch.is_closed:
|
|
334
|
-
await ch.close()
|
|
335
|
-
logger.debug(f"✅ [CLOSE_CHANNEL] 消费者通道已关闭")
|
|
336
|
-
except Exception as e:
|
|
337
|
-
logger.warning(f"❌ [CLOSE_CHANNEL_ERR] 关闭消费者通道失败: {str(e)}")
|
|
385
|
+
logger.info("🛑 [CLOSE] 开始关闭连接池...")
|
|
338
386
|
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
try:
|
|
342
|
-
if not self._channel.is_closed:
|
|
343
|
-
await self._channel.close()
|
|
344
|
-
logger.info(f"✅ [CLOSE_CHANNEL] 主通道已关闭")
|
|
345
|
-
except Exception:
|
|
346
|
-
pass
|
|
347
|
-
self._channel = None
|
|
348
|
-
|
|
349
|
-
# 3. 关闭连接
|
|
350
|
-
# 确保在 finally 或显式 close 中调用 connection.close()
|
|
351
|
-
if self._connection:
|
|
352
|
-
try:
|
|
353
|
-
# 打印关闭操作
|
|
354
|
-
logger.info(f"🔌 [CLOSE_CONN] 正在关闭连接: {id(self._connection)}")
|
|
355
|
-
await self._connection.close()
|
|
356
|
-
logger.info(f"✅ [CLOSE_OK] 连接已成功关闭: {id(self._connection)}")
|
|
357
|
-
except Exception as e:
|
|
358
|
-
logger.warning(f"❌ [CLOSE_ERR] 关闭连接失败: {str(e)}")
|
|
359
|
-
self._connection = None
|
|
387
|
+
# 1. 清理所有资源
|
|
388
|
+
await self._cleanup_resources()
|
|
360
389
|
|
|
361
|
-
logger.info("🏁 [
|
|
390
|
+
logger.info("🏁 [CLOSE] 连接池已关闭")
|
|
362
391
|
|
|
363
|
-
# --- 辅助方法省略 (declare_queue 等) ---
|
|
364
392
|
async def declare_queue(self, queue_name: str, **kwargs) -> AbstractQueue:
|
|
365
393
|
channel, _ = await self.acquire_channel()
|
|
366
394
|
return await channel.declare_queue(queue_name, **kwargs)
|
|
@@ -70,8 +70,8 @@ class RabbitMQCoreService:
|
|
|
70
70
|
virtualhost=cls._config.get('virtual-host', "/"),
|
|
71
71
|
app_name=cls._config.get("APP_NAME", ""),
|
|
72
72
|
prefetch_count=global_prefetch_count,
|
|
73
|
-
heartbeat=cls._config.get('heartbeat',
|
|
74
|
-
connection_timeout=cls._config.get('connection_timeout',
|
|
73
|
+
heartbeat=cls._config.get('heartbeat', 15),
|
|
74
|
+
connection_timeout=cls._config.get('connection_timeout', 15),
|
|
75
75
|
reconnect_interval=cls._config.get('reconnect_interval', 5),
|
|
76
76
|
)
|
|
77
77
|
|
|
@@ -18,7 +18,7 @@ sycommon/health/health_check.py,sha256=EhfbhspRpQiKJaxdtE-PzpKQO_ucaFKtQxIm16F5M
|
|
|
18
18
|
sycommon/health/metrics.py,sha256=fHqO73JuhoZkNPR-xIlxieXiTCvttq-kG-tvxag1s1s,268
|
|
19
19
|
sycommon/health/ping.py,sha256=FTlnIKk5y1mPfS1ZGOeT5IM_2udF5aqVLubEtuBp18M,250
|
|
20
20
|
sycommon/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
sycommon/llm/embedding.py,sha256=
|
|
21
|
+
sycommon/llm/embedding.py,sha256=HknwDqXmRQcAZ8-6d8wZ6n7Bv7HtxTajDt1vvzHGeFQ,8411
|
|
22
22
|
sycommon/llm/get_llm.py,sha256=wawJO_WSLSYPE8ImL421SYBPtAvWqwbRAcUN7d5i0W0,9434
|
|
23
23
|
sycommon/llm/llm_logger.py,sha256=n4UeNy_-g4oHQOsw-VUzF4uo3JVRLtxaMp1FcI8FiEo,5437
|
|
24
24
|
sycommon/llm/llm_tokens.py,sha256=-udDyFcmyzx6UAwIi6_d_wwI5kMd5w0-WcS2soVPQxg,4309
|
|
@@ -47,13 +47,13 @@ sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6
|
|
|
47
47
|
sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
|
|
48
48
|
sycommon/notice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
sycommon/notice/uvicorn_monitor.py,sha256=VryQYcAtjijJuGDBimbVurgwxlsLaLtkNnABPDY5Tao,7332
|
|
50
|
-
sycommon/rabbitmq/rabbitmq_client.py,sha256=
|
|
51
|
-
sycommon/rabbitmq/rabbitmq_pool.py,sha256=
|
|
50
|
+
sycommon/rabbitmq/rabbitmq_client.py,sha256=JZ73fc0Z8iMnayvRhRsnkEkBfzKF3wbxDKTE98RwVIA,17809
|
|
51
|
+
sycommon/rabbitmq/rabbitmq_pool.py,sha256=BiFQgZPzSAFR-n5XhyIafoeWQXETF_31nFRDhMbe6aU,15577
|
|
52
52
|
sycommon/rabbitmq/rabbitmq_service.py,sha256=XSHo9HuIJ_lq-vizRh4xJVdZr_2zLqeLhot09qb0euA,2025
|
|
53
53
|
sycommon/rabbitmq/rabbitmq_service_client_manager.py,sha256=MM4r8Pa2rjAmzy_NpHFb4thGznr6AYk6m__IC8IIxEM,7852
|
|
54
54
|
sycommon/rabbitmq/rabbitmq_service_connection_monitor.py,sha256=uvoMuJDzJ9i63uVRq1NKFV10CvkbGnTMyEoq2rgjQx8,3013
|
|
55
55
|
sycommon/rabbitmq/rabbitmq_service_consumer_manager.py,sha256=489r1RKd5WrTNMAcWCxUZpt9yWGrNunZlLCCp-M_rzM,11497
|
|
56
|
-
sycommon/rabbitmq/rabbitmq_service_core.py,sha256=
|
|
56
|
+
sycommon/rabbitmq/rabbitmq_service_core.py,sha256=6RMvIf78DmEOZmN8dA0duA9oy4ieNswdGrOeyJdD6tU,4753
|
|
57
57
|
sycommon/rabbitmq/rabbitmq_service_producer_manager.py,sha256=TJrLbvsjF55P9lwr7aCm9uRIRuC3z4EZNx74KEVKBtU,10190
|
|
58
58
|
sycommon/sentry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
sycommon/sentry/sy_sentry.py,sha256=e5Fbt9Gi2gIb048z0nuKbuhp3uCAEqYH2xXbF6qrZq4,1471
|
|
@@ -78,8 +78,8 @@ sycommon/tools/env.py,sha256=Ah-tBwG2C0_hwLGFebVQgKdWWXCjTzBuF23gCkLHYy4,2437
|
|
|
78
78
|
sycommon/tools/merge_headers.py,sha256=HV_i52Q-9se3SP8qh7ZGYl8bP7Fxtal4CGVkyMwEdM8,4373
|
|
79
79
|
sycommon/tools/snowflake.py,sha256=lVEe5mNCOgz5OqGQpf5_nXaGnRJlI2STX2s-ppTtanA,11947
|
|
80
80
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
81
|
-
sycommon_python_lib-0.1.
|
|
82
|
-
sycommon_python_lib-0.1.
|
|
83
|
-
sycommon_python_lib-0.1.
|
|
84
|
-
sycommon_python_lib-0.1.
|
|
85
|
-
sycommon_python_lib-0.1.
|
|
81
|
+
sycommon_python_lib-0.1.56b13.dist-info/METADATA,sha256=IpVcf0hWzskjGpow8qRu1kf_Bfb8rErNE-oXJzpcLbA,7270
|
|
82
|
+
sycommon_python_lib-0.1.56b13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
83
|
+
sycommon_python_lib-0.1.56b13.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
84
|
+
sycommon_python_lib-0.1.56b13.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
85
|
+
sycommon_python_lib-0.1.56b13.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.56b11.dist-info → sycommon_python_lib-0.1.56b13.dist-info}/top_level.txt
RENAMED
|
File without changes
|