sycommon-python-lib 0.1.43__py3-none-any.whl → 0.1.45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  import asyncio
2
- from typing import List, Optional, Set, Tuple, cast
3
- from aio_pika import connect_robust, Channel
4
- from aio_pika.abc import AbstractRobustConnection, AbstractConnection
2
+ from typing import Optional, List, Set, Iterator, Tuple
3
+ from aio_pika import connect_robust, Channel, Message
4
+ from aio_pika.abc import (
5
+ AbstractRobustConnection, AbstractQueue, AbstractExchange, AbstractMessage
6
+ )
5
7
 
6
8
  from sycommon.logging.kafka_log import SYLogger
7
9
 
@@ -9,7 +11,7 @@ logger = SYLogger
9
11
 
10
12
 
11
13
  class RabbitMQConnectionPool:
12
- """增强版连接池,支持连接自动重建、通道有效性校验"""
14
+ """单连接RabbitMQ通道池(严格单连接)"""
13
15
 
14
16
  def __init__(
15
17
  self,
@@ -18,349 +20,385 @@ class RabbitMQConnectionPool:
18
20
  username: str,
19
21
  password: str,
20
22
  virtualhost: str = "/",
21
- connection_pool_size: int = 2,
22
- channel_pool_size: int = 5,
23
+ channel_pool_size: int = 1,
23
24
  heartbeat: int = 30,
24
25
  app_name: str = "",
25
- reconnect_interval: int = 15,
26
- connection_timeout: int = 10,
26
+ connection_timeout: int = 30,
27
+ reconnect_interval: int = 30,
28
+ prefetch_count: int = 2,
27
29
  ):
28
30
  self.hosts = [host.strip() for host in hosts if host.strip()]
29
31
  if not self.hosts:
30
32
  raise ValueError("至少需要提供一个RabbitMQ主机地址")
33
+
34
+ # 连接配置(所有通道共享此连接的配置)
31
35
  self.port = port
32
36
  self.username = username
33
37
  self.password = password
34
38
  self.virtualhost = virtualhost
35
39
  self.app_name = app_name or "rabbitmq-client"
36
40
  self.heartbeat = heartbeat
37
- self.reconnect_interval = reconnect_interval
38
41
  self.connection_timeout = connection_timeout
39
-
40
- # 连接池配置
41
- self.connection_pool_size = connection_pool_size
42
+ self.reconnect_interval = reconnect_interval
43
+ self.prefetch_count = prefetch_count
42
44
  self.channel_pool_size = channel_pool_size
43
45
 
44
- # 实际存储的连接和通道(用元组存储连接+最后活动时间,便于清理超时连接)
45
- self._connections: List[Tuple[AbstractRobustConnection, float]] = []
46
- self._free_channels: List[Tuple[Channel,
47
- AbstractRobustConnection]] = [] # 通道绑定所属连接
48
- self._used_channels: Set[Tuple[Channel,
49
- AbstractRobustConnection]] = set()
46
+ # 节点轮询:仅用于连接失效时切换节点(仍保持单连接)
47
+ self._host_iterator: Iterator[str] = self._create_host_iterator()
48
+ self._current_host: Optional[str] = None # 当前连接的节点
50
49
 
51
- # 锁用于线程安全
52
- self._conn_lock = asyncio.Lock()
53
- self._chan_lock = asyncio.Lock()
50
+ # 核心资源(严格单连接 + 通道池)
51
+ self._connection: Optional[AbstractRobustConnection] = None # 唯一连接
52
+ self._free_channels: List[Channel] = [] # 通道池(仅存储当前连接的通道)
53
+ self._used_channels: Set[Channel] = set()
54
54
 
55
- # 连接状态
55
+ # 状态控制(确保线程安全)
56
+ self._lock = asyncio.Lock()
56
57
  self._initialized = False
57
- self._reconnect_task: Optional[asyncio.Task] = None
58
58
  self._is_shutdown = False
59
+ self._reconnecting = False # 避免重连并发冲突
60
+
61
+ def _create_host_iterator(self) -> Iterator[str]:
62
+ """创建节点轮询迭代器(无限循环,仅用于切换节点)"""
63
+ while True:
64
+ for host in self.hosts:
65
+ yield host
59
66
 
60
67
  @property
61
68
  def is_alive(self) -> bool:
62
- if not self._initialized:
69
+ """检查唯一连接是否存活(使用is_closed判断,兼容所有版本)"""
70
+ if not self._initialized or not self._connection:
63
71
  return False
64
- # 异步清理失效连接(不阻塞当前调用)
65
- asyncio.create_task(self._check_connections())
66
- # 同步校验存活连接(即使清理未完成,也能反映当前状态)
67
- return any(not conn.is_closed for conn, _ in self._connections)
72
+ # 异步清理失效通道(不影响主流程)
73
+ asyncio.create_task(self._clean_invalid_channels())
74
+ return not self._connection.is_closed
75
+
76
+ async def _safe_close_resources(self):
77
+ """安全关闭资源:先关通道,再关连接(保证单连接特性)"""
78
+ async with self._lock:
79
+ # 1. 关闭所有通道(无论空闲还是使用中)
80
+ all_channels = self._free_channels + list(self._used_channels)
81
+ for channel in all_channels:
82
+ try:
83
+ if not channel.is_closed:
84
+ await channel.close()
85
+ except Exception as e:
86
+ logger.warning(f"关闭通道失败: {str(e)}")
87
+ self._free_channels.clear()
88
+ self._used_channels.clear()
68
89
 
69
- async def _check_connections(self):
70
- """异步检查连接有效性(清理已关闭的连接)"""
71
- async with self._conn_lock:
72
- self._connections = [
73
- (conn, ts) for conn, ts in self._connections if not conn.is_closed]
90
+ # 2. 关闭唯一连接
91
+ if self._connection:
92
+ try:
93
+ if not self._connection.is_closed:
94
+ await self._connection.close()
95
+ logger.info(f"已关闭唯一连接: {self._current_host}:{self.port}")
96
+ except Exception as e:
97
+ logger.warning(f"关闭连接失败: {str(e)}")
98
+ self._connection = None # 置空,确保单连接
74
99
 
75
- async def init_pools(self):
76
- """初始化连接池+启动连接监控任务"""
77
- if self._initialized:
78
- logger.warning("连接池已初始化,无需重复调用")
79
- return
100
+ async def _create_single_connection(self) -> AbstractRobustConnection:
101
+ """创建唯一连接(失败时轮询节点,切换前关闭旧连接)"""
102
+ max_attempts = len(self.hosts) # 每个节点尝试1次
103
+ attempts = 0
104
+ last_error: Optional[Exception] = None
80
105
 
81
- try:
82
- # 创建初始连接
83
- await self._create_initial_connections()
84
- # 启动连接监控任务(后台检查并重建失效连接)
85
- self._reconnect_task = asyncio.create_task(
86
- self._monitor_connections())
87
- self._initialized = True
88
- logger.info(
89
- f"RabbitMQ连接池初始化成功 - 连接数: {len(self._connections)}, "
90
- f"空闲通道数: {len(self._free_channels)}, 集群节点: {self.hosts}"
91
- )
92
- except Exception as e:
93
- logger.error(f"连接池初始化失败: {str(e)}", exc_info=True)
94
- await self.close()
95
- raise
106
+ while attempts < max_attempts and not self._is_shutdown:
107
+ next_host = next(self._host_iterator)
96
108
 
97
- async def _create_initial_connections(self):
98
- """创建初始连接和通道"""
99
- for i in range(self.connection_pool_size):
100
- try:
101
- conn = await self._create_single_connection()
102
- self._connections.append(
103
- (conn, asyncio.get_event_loop().time()))
104
- # 为每个连接创建初始通道
105
- chan_count_per_conn = self.channel_pool_size // self.connection_pool_size
106
- for _ in range(chan_count_per_conn):
107
- chan = await conn.channel()
108
- self._free_channels.append((chan, conn))
109
- except Exception as e:
110
- logger.error(f"创建初始连接/通道失败(第{i+1}个): {str(e)}", exc_info=True)
111
- # 允许部分连接失败,后续监控任务会重试
112
- continue
109
+ # 切换节点前:强制关闭旧连接(保证单连接)
110
+ if self._connection:
111
+ await self._safe_close_resources()
113
112
 
114
- async def _create_single_connection(self) -> AbstractRobustConnection:
115
- hosts = self.hosts.copy()
116
- retry_count = 0
117
- max_retries = 3 # 每个节点最多重试3次
118
-
119
- while retry_count < max_retries and not self._is_shutdown:
120
- if not hosts:
121
- hosts = self.hosts.copy()
122
- retry_count += 1
123
- if retry_count >= max_retries:
124
- logger.error(
125
- f"所有RabbitMQ节点({self.hosts})均连接失败,已重试{max_retries}次,将在15秒后再次尝试"
126
- )
127
- # 固定15秒间隔后退出,由监控任务触发下一次重试
128
- await asyncio.sleep(self.reconnect_interval)
129
- break
130
-
131
- host = hosts.pop(0)
132
- conn_url = (
133
- f"amqp://{self.username}:{self.password}@{host}:{self.port}/{self.virtualhost}"
134
- f"?heartbeat={self.heartbeat}&timeout={self.connection_timeout}"
135
- )
113
+ self._current_host = next_host
114
+ conn_url = f"amqp://{self.username}:{self.password}@{self._current_host}:{self.port}/{self.virtualhost}"
136
115
 
137
116
  try:
117
+ logger.info(f"尝试创建唯一连接: {self._current_host}:{self.port}")
138
118
  conn = await connect_robust(
139
119
  conn_url,
140
- client_properties={
141
- "connection_name": f"{self.app_name}@{host}"
120
+ properties={
121
+ "connection_name": f"{self.app_name}_single_conn",
122
+ "product": self.app_name
142
123
  },
143
- reconnect_interval=self.reconnect_interval, # 客户端内置重连间隔也设为15秒
124
+ heartbeat=self.heartbeat,
125
+ timeout=self.connection_timeout,
126
+ reconnect_interval=self.reconnect_interval,
127
+ max_reconnect_attempts=None, # 单节点内部自动重连
144
128
  )
145
-
146
- # 连接关闭回调(固定间隔重连)
147
- def on_connection_closed(conn_instance: AbstractConnection, exc: Optional[BaseException]):
148
- logger.warning(
149
- f"RabbitMQ连接关闭: {conn_instance!r},原因: {exc}", exc_info=exc)
150
- asyncio.create_task(
151
- self._remove_invalid_connection(cast(AbstractRobustConnection, conn_instance)))
152
-
153
- setattr(conn, '_pool_close_callback', on_connection_closed)
154
- conn.close_callbacks.add(on_connection_closed)
155
-
156
- logger.info(f"成功连接到RabbitMQ节点: {host}:{self.port}")
129
+ logger.info(f"唯一连接创建成功: {self._current_host}:{self.port}")
157
130
  return conn
158
131
  except Exception as e:
159
- logger.warning(
160
- f"连接节点 {host}:{self.port} 失败(重试{retry_count}/{max_retries}): {str(e)}"
132
+ attempts += 1
133
+ last_error = e
134
+ logger.error(
135
+ f"连接节点 {self._current_host}:{self.port} 失败({attempts}/{max_attempts}): {str(e)}",
136
+ exc_info=True
161
137
  )
162
- # 每个节点连接失败后,固定等待15秒再尝试下一个节点
163
- await asyncio.sleep(self.reconnect_interval)
138
+ await asyncio.sleep(30) # 避免频繁重试
164
139
 
165
- raise RuntimeError(
166
- f"所有RabbitMQ节点连接失败(已重试{max_retries}次),节点列表: {self.hosts}"
167
- )
140
+ raise ConnectionError(
141
+ f"所有节点创建唯一连接失败(节点列表: {self.hosts}"
142
+ ) from last_error
168
143
 
169
- async def _remove_invalid_connection(self, invalid_conn: AbstractRobustConnection) -> None:
170
- """移除失效连接及关联通道"""
171
- try:
172
- # 关键修复:移除连接关闭回调
173
- callback = getattr(invalid_conn, '_pool_close_callback', None)
174
- if callback:
175
- invalid_conn.close_callbacks.discard(callback)
176
- delattr(invalid_conn, '_pool_close_callback', None)
177
- except Exception as e:
178
- logger.warning(f"移除连接回调失败: {str(e)}")
179
- # 1. 移除失效连接
180
- async with self._conn_lock:
181
- self._connections = [
182
- (conn, ts) for conn, ts in self._connections if conn != invalid_conn
183
- ]
184
- # 2. 移除该连接关联的所有通道
185
- async with self._chan_lock:
186
- self._free_channels = [
187
- (chan, conn) for chan, conn in self._free_channels if conn != invalid_conn
188
- ]
189
- self._used_channels = {
190
- (chan, conn) for chan, conn in self._used_channels if conn != invalid_conn
191
- }
192
- # 3. 触发连接重建
193
- asyncio.create_task(self._recreate_connection())
194
-
195
- async def _recreate_connection(self):
196
- """重建连接:固定间隔重试"""
197
- try:
198
- # 重建前检查是否已达到连接池上限
199
- async with self._conn_lock:
200
- if len(self._connections) >= self.connection_pool_size:
201
- logger.debug("连接池已达最大限制,跳过重建连接")
202
- return
144
+ async def _init_channel_pool(self):
145
+ """初始化通道池(绑定到唯一连接,仅创建指定数量的通道)"""
146
+ if not self._connection or self._connection.is_closed:
147
+ raise RuntimeError("无有效连接,无法初始化通道池")
148
+
149
+ async with self._lock:
150
+ self._free_channels.clear()
151
+ self._used_channels.clear()
203
152
 
204
- conn = await self._create_single_connection()
205
- async with self._conn_lock:
206
- self._connections.append(
207
- (conn, asyncio.get_event_loop().time()))
208
- # 补充通道
209
- chan_count_per_conn = self.channel_pool_size // self.connection_pool_size
210
- for _ in range(chan_count_per_conn):
153
+ # 创建指定数量的通道(池大小由channel_pool_size控制)
154
+ for i in range(self.channel_pool_size):
211
155
  try:
212
- chan = await conn.channel()
213
- async with self._chan_lock:
214
- self._free_channels.append((chan, conn))
156
+ channel = await self._connection.channel()
157
+ await channel.set_qos(prefetch_count=self.prefetch_count)
158
+ self._free_channels.append(channel)
215
159
  except Exception as e:
216
- logger.warning(f"为新连接创建通道失败: {str(e)}")
160
+ logger.error(f"创建通道失败(第{i+1}个): {str(e)}", exc_info=True)
161
+ # 通道创建失败不中断,继续创建剩余通道
162
+ continue
163
+
164
+ logger.info(
165
+ f"通道池初始化完成 - 连接: {self._current_host}:{self.port}, "
166
+ f"可用通道数: {len(self._free_channels)}/{self.channel_pool_size}"
167
+ )
168
+
169
+ async def _reconnect_if_needed(self) -> bool:
170
+ """连接失效时重连(保证单连接)"""
171
+ if self._is_shutdown or self._reconnecting:
172
+ return False
173
+
174
+ self._reconnecting = True
175
+ try:
176
+ logger.warning("连接失效,开始重连...")
177
+ # 重新创建唯一连接
178
+ self._connection = await self._create_single_connection()
179
+ # 重新初始化通道池
180
+ await self._init_channel_pool()
181
+ logger.info("重连成功,通道池已恢复")
182
+ return True
217
183
  except Exception as e:
218
- logger.error(f"重建连接失败: {str(e)}", exc_info=True)
219
- # 重建失败后,15秒后再次尝试
220
- if not self._is_shutdown:
221
- asyncio.create_task(self._recreate_connection())
222
-
223
- async def _monitor_connections(self):
224
- """后台监控:固定15秒检查一次连接状态"""
225
- while self._initialized and not self._is_shutdown:
226
- try:
227
- await asyncio.sleep(self.reconnect_interval) # 固定15秒间隔检查
228
- current_time = asyncio.get_event_loop().time()
229
-
230
- # 清理失效/超时连接
231
- async with self._conn_lock:
232
- valid_connections = []
233
- for conn, last_active in self._connections:
234
- if conn.is_closed or (current_time - last_active) > 600: # 10分钟无活动清理
235
- logger.warning(f"清理失效/超时连接: {conn}")
236
- try:
237
- # 移除回调+关闭连接
238
- callback = getattr(
239
- conn, '_pool_close_callback', None)
240
- if callback:
241
- conn.close_callbacks.discard(callback)
242
- await conn.close()
243
- except:
244
- pass
245
- else:
246
- valid_connections.append((conn, last_active))
247
- self._connections = valid_connections
248
-
249
- # 补充缺失的连接(不超过连接池最大限制)
250
- missing_conn_count = self.connection_pool_size - \
251
- len(self._connections)
252
- if missing_conn_count > 0:
253
- logger.info(f"连接池缺少{missing_conn_count}个连接,尝试补充")
254
- # 逐个补充,避免同时创建大量连接
255
- for _ in range(missing_conn_count):
256
- asyncio.create_task(self._recreate_connection())
257
- except Exception as e:
258
- logger.error(f"连接监控任务异常: {str(e)}", exc_info=True)
259
- # 异常后仍保持15秒间隔
260
- await asyncio.sleep(self.reconnect_interval)
184
+ logger.error(f"重连失败: {str(e)}", exc_info=True)
185
+ self._initialized = False # 重连失败后标记未初始化
186
+ return False
187
+ finally:
188
+ self._reconnecting = False
261
189
 
262
- async def acquire_channel(self) -> Tuple[Channel, AbstractRobustConnection]:
263
- """获取通道(返回通道+所属连接,便于释放时校验)"""
264
- if not self._initialized:
265
- raise RuntimeError("连接池未初始化,请先调用 init_pools()")
266
-
267
- async with self._chan_lock:
268
- # 优先从空闲通道池获取有效通道
269
- for i in range(len(self._free_channels)-1, -1, -1):
270
- chan, conn = self._free_channels[i]
271
- # 校验通道和连接是否有效
272
- if not conn.is_closed and not chan.is_closed:
273
- # 更新连接最后活动时间
274
- async with self._conn_lock:
275
- for j, (c, ts) in enumerate(self._connections):
276
- if c == conn:
277
- self._connections[j] = (
278
- c, asyncio.get_event_loop().time())
279
- break
280
- # 移到已使用通道集合
281
- self._free_channels.pop(i)
282
- self._used_channels.add((chan, conn))
283
- return chan, conn
284
- else:
285
- # 移除无效通道
286
- self._free_channels.pop(i)
287
- logger.warning("清理无效空闲通道")
288
-
289
- # 空闲通道不足,创建新通道
290
- if len(self._used_channels) < self.channel_pool_size:
291
- # 选择一个有效连接创建通道
292
- async with self._conn_lock:
293
- for conn, _ in self._connections:
294
- if not conn.is_closed:
295
- try:
296
- chan = await conn.channel()
297
- self._used_channels.add((chan, conn))
298
- logger.info(
299
- f"创建新通道,当前通道数: {len(self._used_channels)}/{self.channel_pool_size}")
300
- return chan, conn
301
- except Exception as e:
302
- logger.warning(f"使用连接创建通道失败: {str(e)}")
303
- # 无有效连接,尝试创建新连接
190
+ async def _clean_invalid_channels(self):
191
+ """清理失效通道并补充(仅针对当前唯一连接)"""
192
+ if not self._connection:
193
+ return
194
+
195
+ async with self._lock:
196
+ # 1. 清理空闲通道中的失效通道
197
+ valid_free = [
198
+ chan for chan in self._free_channels if not chan.is_closed]
199
+ invalid_count = len(self._free_channels) - len(valid_free)
200
+ if invalid_count > 0:
201
+ logger.warning(f"清理{invalid_count}个失效空闲通道")
202
+ self._free_channels = valid_free
203
+
204
+ # 2. 清理使用中通道中的失效通道
205
+ valid_used = {
206
+ chan for chan in self._used_channels if not chan.is_closed}
207
+ invalid_used_count = len(self._used_channels) - len(valid_used)
208
+ if invalid_used_count > 0:
209
+ logger.warning(f"清理{invalid_used_count}个失效使用中通道")
210
+ self._used_channels = valid_used
211
+
212
+ # 3. 检查连接是否有效,无效则触发重连
213
+ if self._connection.is_closed:
214
+ await self._reconnect_if_needed()
215
+ return
216
+
217
+ # 4. 补充通道到指定大小(仅使用当前唯一连接创建)
218
+ total_valid = len(self._free_channels) + len(self._used_channels)
219
+ missing = self.channel_pool_size - total_valid
220
+ if missing > 0:
221
+ logger.info(f"通道池缺少{missing}个通道,补充中...")
222
+ for _ in range(missing):
304
223
  try:
305
- conn = await self._create_single_connection()
306
- self._connections.append(
307
- (conn, asyncio.get_event_loop().time()))
308
- chan = await conn.channel()
309
- self._used_channels.add((chan, conn))
310
- return chan, conn
224
+ channel = await self._connection.channel()
225
+ await channel.set_qos(prefetch_count=self.prefetch_count)
226
+ self._free_channels.append(channel)
311
227
  except Exception as e:
312
- logger.error(f"创建新连接+通道失败: {str(e)}", exc_info=True)
313
- raise RuntimeError("无可用连接创建通道")
314
- else:
315
- raise RuntimeError(f"通道池已达最大限制: {self.channel_pool_size}")
228
+ logger.error(f"补充通道失败: {str(e)}", exc_info=True)
229
+ break
230
+
231
+ async def init_pools(self):
232
+ """初始化:创建唯一连接 + 初始化通道池(仅执行一次)"""
233
+ if self._initialized:
234
+ logger.warning("通道池已初始化,无需重复调用")
235
+ return
236
+
237
+ if self._is_shutdown:
238
+ raise RuntimeError("通道池已关闭,无法初始化")
239
+
240
+ try:
241
+ # 1. 创建唯一连接
242
+ self._connection = await self._create_single_connection()
243
+ # 2. 初始化通道池(绑定到该连接)
244
+ await self._init_channel_pool()
245
+ self._initialized = True
246
+ logger.info("RabbitMQ单连接通道池初始化完成")
247
+ except Exception as e:
248
+ logger.error(f"初始化失败: {str(e)}", exc_info=True)
249
+ await self._safe_close_resources()
250
+ raise
251
+
252
+ async def acquire_channel(self) -> Tuple[Channel, AbstractRobustConnection]:
253
+ """获取通道(返回元组:(通道, 唯一连接),兼容上层代码)"""
254
+ if not self._initialized:
255
+ raise RuntimeError("通道池未初始化,请先调用init_pools()")
256
+
257
+ if self._is_shutdown:
258
+ raise RuntimeError("通道池已关闭,无法获取通道")
259
+
260
+ # 先清理失效通道,确保池内通道有效
261
+ await self._clean_invalid_channels()
262
+
263
+ async with self._lock:
264
+ # 优先从空闲池获取
265
+ if self._free_channels:
266
+ channel = self._free_channels.pop()
267
+ self._used_channels.add(channel)
268
+ # 返回(通道, 唯一连接)元组
269
+ return channel, self._connection
270
+
271
+ # 通道池已满,创建临时通道(超出池大小,用完关闭)
272
+ try:
273
+ if not self._connection or self._connection.is_closed:
274
+ raise RuntimeError("唯一连接已失效,无法创建临时通道")
275
+
276
+ channel = await self._connection.channel()
277
+ await channel.set_qos(prefetch_count=self.prefetch_count)
278
+ self._used_channels.add(channel)
279
+ logger.warning(
280
+ f"通道池已达上限({self.channel_pool_size}),创建临时通道(用完自动关闭)"
281
+ )
282
+ # 返回(通道, 唯一连接)元组
283
+ return channel, self._connection
284
+ except Exception as e:
285
+ logger.error(f"获取通道失败: {str(e)}", exc_info=True)
286
+ raise
316
287
 
317
288
  async def release_channel(self, channel: Channel, conn: AbstractRobustConnection):
318
- """释放通道(校验有效性后归还)"""
319
- async with self._chan_lock:
320
- key = (channel, conn)
321
- if key in self._used_channels:
322
- self._used_channels.remove(key)
323
- # 通道和连接都有效才归还
324
- if not conn.is_closed and not channel.is_closed:
325
- self._free_channels.append(key)
326
- else:
327
- logger.warning("释放无效通道,已自动丢弃")
289
+ """释放通道(接收通道和连接参数,兼容上层代码)"""
290
+ if not channel or not conn or self._is_shutdown:
291
+ return
328
292
 
329
- async def close(self):
330
- """关闭连接池+监控任务"""
331
- self._initialized = False
332
- # 停止监控任务
333
- if self._reconnect_task and not self._reconnect_task.done():
334
- self._reconnect_task.cancel()
293
+ # 仅处理当前唯一连接的通道(避免无效连接的通道)
294
+ if conn != self._connection:
335
295
  try:
336
- await self._reconnect_task
337
- except asyncio.CancelledError:
338
- pass
296
+ await channel.close()
297
+ logger.warning("已关闭非当前连接的通道(可能是重连后的旧通道)")
298
+ except Exception as e:
299
+ logger.warning(f"关闭非当前连接通道失败: {str(e)}")
300
+ return
301
+
302
+ async with self._lock:
303
+ if channel not in self._used_channels:
304
+ return
339
305
 
340
- # 释放所有通道
341
- async with self._chan_lock:
342
- for channel, conn in self._free_channels + list(self._used_channels):
306
+ self._used_channels.remove(channel)
307
+
308
+ # 仅归还:当前连接有效 + 通道未关闭 + 池未满
309
+ if (not self._connection.is_closed
310
+ and not channel.is_closed
311
+ and len(self._free_channels) < self.channel_pool_size):
312
+ self._free_channels.append(channel)
313
+ else:
314
+ # 无效通道直接关闭
343
315
  try:
344
- if not channel.is_closed:
345
- await channel.close()
316
+ await channel.close()
346
317
  except Exception as e:
347
318
  logger.warning(f"关闭通道失败: {str(e)}")
348
- self._free_channels.clear()
349
- self._used_channels.clear()
350
319
 
351
- # 关闭所有连接
352
- async with self._conn_lock:
353
- for conn, _ in self._connections:
354
- try:
355
- # 移除所有连接的回调
356
- callback = getattr(conn, '_pool_close_callback', None)
357
- if callback:
358
- conn.close_callbacks.discard(callback)
359
- delattr(conn, '_pool_close_callback', None)
360
- if not conn.is_closed:
361
- await conn.close()
362
- except Exception as e:
363
- logger.warning(f"关闭连接失败: {str(e)}")
364
- self._connections.clear()
320
+ async def declare_queue(self, queue_name: str, **kwargs) -> AbstractQueue:
321
+ """声明队列(使用池内通道,共享唯一连接)"""
322
+ channel, conn = await self.acquire_channel()
323
+ try:
324
+ return await channel.declare_queue(queue_name, **kwargs)
325
+ finally:
326
+ await self.release_channel(channel, conn)
327
+
328
+ async def declare_exchange(self, exchange_name: str, exchange_type: str = "direct", **kwargs) -> AbstractExchange:
329
+ """声明交换机(使用池内通道,共享唯一连接)"""
330
+ channel, conn = await self.acquire_channel()
331
+ try:
332
+ return await channel.declare_exchange(exchange_name, exchange_type, **kwargs)
333
+ finally:
334
+ await self.release_channel(channel, conn)
335
+
336
+ async def publish_message(self, routing_key: str, message_body: bytes, exchange_name: str = "", **kwargs):
337
+ """发布消息(使用池内通道,共享唯一连接)"""
338
+ channel, conn = await self.acquire_channel()
339
+ try:
340
+ exchange = channel.default_exchange if not exchange_name else await channel.get_exchange(exchange_name)
341
+ message = Message(body=message_body, **kwargs)
342
+ await exchange.publish(message, routing_key=routing_key)
343
+ logger.debug(
344
+ f"消息发布成功 - 节点: {self._current_host}, 交换机: {exchange.name}, 路由键: {routing_key}"
345
+ )
346
+ except Exception as e:
347
+ logger.error(f"发布消息失败: {str(e)}", exc_info=True)
348
+ raise
349
+ finally:
350
+ await self.release_channel(channel, conn)
351
+
352
+ async def consume_queue(self, queue_name: str, callback, auto_ack: bool = False, **kwargs):
353
+ """消费队列(使用池内通道,共享唯一连接)"""
354
+ if not self._initialized:
355
+ raise RuntimeError("通道池未初始化,请先调用init_pools()")
356
+
357
+ queue = await self.declare_queue(queue_name, **kwargs)
358
+ current_channel, current_conn = await self.acquire_channel() # 元组解包
359
+
360
+ async def consume_callback_wrapper(message: AbstractMessage):
361
+ """消费回调包装(处理通道失效重连)"""
362
+ nonlocal current_channel, current_conn
363
+ try:
364
+ # 检查通道是否有效(连接可能已切换)
365
+ if (current_channel.is_closed
366
+ or current_conn.is_closed
367
+ or current_conn != self._connection):
368
+ logger.warning("消费通道失效,重新获取通道...")
369
+ await self.release_channel(current_channel, current_conn)
370
+ current_channel, current_conn = await self.acquire_channel()
371
+ return
372
+
373
+ await callback(message)
374
+ if not auto_ack:
375
+ await message.ack()
376
+ except Exception as e:
377
+ logger.error(f"消费消息失败: {str(e)}", exc_info=True)
378
+ if not auto_ack:
379
+ await message.nack(requeue=True)
380
+
381
+ logger.info(f"开始消费队列: {queue_name}(连接节点: {self._current_host})")
382
+ try:
383
+ async with queue.iterator() as queue_iter:
384
+ async for message in queue_iter:
385
+ if self._is_shutdown:
386
+ logger.info("消费已停止,退出消费循环")
387
+ break
388
+ await consume_callback_wrapper(message)
389
+ finally:
390
+ await self.release_channel(current_channel, current_conn)
391
+
392
+ async def close(self):
393
+ """关闭通道池:释放所有通道 + 关闭唯一连接"""
394
+ if self._is_shutdown:
395
+ logger.warning("通道池已关闭,无需重复操作")
396
+ return
397
+
398
+ self._is_shutdown = True
399
+ logger.info("开始关闭RabbitMQ单连接通道池...")
400
+
401
+ # 安全释放所有资源
402
+ await self._safe_close_resources()
365
403
 
366
- logger.info("RabbitMQ连接池已完全关闭")
404
+ logger.info("RabbitMQ单连接通道池已完全关闭")