sycommon-python-lib 0.1.48__py3-none-any.whl → 0.1.50__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,6 +1,7 @@
1
1
  import asyncio
2
- from typing import Optional, List, Set, Iterator, Tuple, Dict, Callable
3
- from aio_pika import connect_robust, Channel, Message
2
+ import random
3
+ from typing import Optional, List, Dict, Callable, Tuple
4
+ from aio_pika import connect_robust, RobustChannel, Message
4
5
  from aio_pika.abc import (
5
6
  AbstractRobustConnection, AbstractQueue, AbstractExchange, AbstractMessage
6
7
  )
@@ -13,7 +14,7 @@ logger = SYLogger
13
14
 
14
15
 
15
16
  class RabbitMQConnectionPool:
16
- """单连接RabbitMQ通道池(核心特性:严格单连接+重连释放旧资源+新连接保留自动恢复+全场景加锁)"""
17
+ """单连接单通道RabbitMQ客户端(核心特性:依赖connect_robust原生自动重连/恢复 + 仅关闭时释放资源)"""
17
18
 
18
19
  def __init__(
19
20
  self,
@@ -22,11 +23,10 @@ class RabbitMQConnectionPool:
22
23
  username: str,
23
24
  password: str,
24
25
  virtualhost: str = "/",
25
- channel_pool_size: int = 1,
26
26
  heartbeat: int = 30,
27
27
  app_name: str = "",
28
28
  connection_timeout: int = 30,
29
- reconnect_interval: int = 30,
29
+ reconnect_interval: int = 5,
30
30
  prefetch_count: int = 2,
31
31
  ):
32
32
  # 基础配置校验与初始化
@@ -43,482 +43,206 @@ class RabbitMQConnectionPool:
43
43
  self.connection_timeout = connection_timeout
44
44
  self.reconnect_interval = reconnect_interval
45
45
  self.prefetch_count = prefetch_count
46
- self.channel_pool_size = max(1, channel_pool_size) # 确保池大小不小于1
47
46
 
48
- # 节点轮询(仅重连时切换)
49
- self._host_iterator: Iterator[str] = self._create_host_iterator()
50
- self._current_host: Optional[str] = None
47
+ # 初始化时随机选择一个主机地址(固定使用,依赖原生重连)
48
+ self._current_host: str = random.choice(self.hosts)
49
+ logger.info(
50
+ f"随机选择RabbitMQ主机: {self._current_host}(依赖connect_robust原生自动重连/恢复)")
51
51
 
52
- # 核心资源(严格单连接+通道池,仅绑定当前活跃连接)
53
- self._connection: Optional[AbstractRobustConnection] = None # 唯一活跃连接
54
- self._free_channels: List[Channel] = [] # 当前连接的空闲通道(带自动恢复)
55
- self._used_channels: Set[Channel] = set() # 当前连接的使用中通道(带自动恢复)
56
- self._consumer_channels: Dict[str, Tuple[Channel,
57
- AbstractRobustConnection, Callable, bool, dict]] = {} # 消费者通道跟踪
52
+ # 核心资源(单连接+单通道,基于原生自动重连)
53
+ self._connection: Optional[AbstractRobustConnection] = None # 原生自动重连连接
54
+ self._channel: Optional[RobustChannel] = None # 单通道(原生自动恢复)
55
+ # 消费者通道跟踪(独立于主通道)
56
+ self._consumer_channels: Dict[str,
57
+ Tuple[RobustChannel, Callable, bool, dict]] = {}
58
58
 
59
- # 状态控制(确保并发安全和单连接)
60
- self._lock = asyncio.Lock() # 全局唯一锁,保护所有共享状态
59
+ # 状态控制(并发安全+生命周期管理)
60
+ self._lock = asyncio.Lock()
61
61
  self._initialized = False
62
62
  self._is_shutdown = False
63
- self._reconnecting = False # 避免并发重连
64
- self._connection_version = 0 # 连接版本号(区分新旧连接/通道)
65
-
66
- def _create_host_iterator(self) -> Iterator[str]:
67
- """创建无限循环的节点轮询迭代器"""
68
- while True:
69
- for host in self.hosts:
70
- yield host
71
63
 
72
64
  async def _is_connection_valid(self) -> bool:
73
- """原子化检查连接有效性(加锁保证无竞态)"""
65
+ """原子化检查连接有效性(所有状态判断均加锁,确保原子性)"""
74
66
  async with self._lock:
75
- return (
76
- self._connection is not None
77
- and not self._connection.is_closed
78
- and not self._reconnecting
79
- )
67
+ # 优先级:先判断是否关闭,再判断是否初始化,最后判断连接状态
68
+ return not self._is_shutdown and self._initialized and self._connection is not None and not self._connection.is_closed
80
69
 
81
70
  @property
82
71
  async def is_alive(self) -> bool:
83
- """对外暴露的连接存活状态(异步+原子化)"""
84
- if self._is_shutdown:
85
- return False
86
- return await self._is_connection_valid()
87
-
88
- async def _safe_close_old_resources(self):
89
- """强制关闭所有旧资源(加锁保证原子性,重连前必调用)"""
72
+ """对外暴露的连接存活状态(原子化判断)"""
90
73
  async with self._lock:
91
- logger.info(f"开始释放旧资源(连接版本: {self._connection_version})...")
92
-
93
- # 1. 关闭所有消费者通道(独立管理,终止旧自动恢复)
94
- for queue_name, (channel, _, _, _, _) in self._consumer_channels.items():
95
- try:
96
- if not channel.is_closed:
97
- await channel.close()
98
- logger.info(f"已关闭队列 {queue_name} 的旧消费者通道(自动恢复终止)")
99
- except Exception as e:
100
- logger.warning(f"关闭消费者通道 {queue_name} 失败: {str(e)}")
101
- self._consumer_channels.clear()
102
-
103
- # 2. 关闭所有普通通道(空闲+使用中,终止旧自动恢复)
104
- all_channels = self._free_channels + list(self._used_channels)
105
- for channel in all_channels:
106
- try:
107
- if not channel.is_closed:
108
- await channel.close()
109
- except Exception as e:
110
- logger.warning(f"关闭旧通道失败: {str(e)}")
111
- self._free_channels.clear()
112
- self._used_channels.clear()
113
-
114
- # 3. 强制关闭旧连接(彻底终止旧连接的所有自动恢复)
115
- if self._connection:
116
- try:
117
- if not self._connection.is_closed:
118
- await self._connection.close()
119
- logger.info(
120
- f"已关闭旧连接: {self._current_host}:{self.port}(版本: {self._connection_version})")
121
- except Exception as e:
122
- logger.warning(f"关闭旧连接失败: {str(e)}")
123
- self._connection = None # 置空,确保单连接
124
-
125
- logger.info("旧资源释放完成(所有旧自动恢复逻辑已终止)")
126
-
127
- async def _create_single_connection(self) -> AbstractRobustConnection:
128
- """创建唯一活跃连接(重连前已释放旧资源,确保单连接)"""
129
- max_attempts = len(self.hosts) * 2 # 每个节点尝试2次
130
- attempts = 0
131
- last_error: Optional[Exception] = None
132
-
133
- while attempts < max_attempts and not self._is_shutdown:
134
- self._current_host = next(self._host_iterator)
135
- conn_url = f"amqp://{self.username}:{self.password}@{self._current_host}:{self.port}/{self.virtualhost}"
136
-
137
- try:
138
- target_version = self._connection_version + 1
139
- logger.info(
140
- f"尝试创建连接: {self._current_host}:{self.port} "
141
- f"(目标版本: {target_version},{attempts+1}/{max_attempts})"
142
- )
143
- # 创建连接(保留aio-pika原生自动恢复)
144
- conn = await connect_robust(
145
- conn_url,
146
- properties={
147
- "connection_name": f"{self.app_name}_conn_v{target_version}",
148
- "product": self.app_name
149
- },
150
- heartbeat=self.heartbeat,
151
- timeout=self.connection_timeout,
152
- reconnect_interval=5, # 单节点内部短间隔重连(原生自动恢复)
153
- max_reconnect_attempts=3, # 单节点最大重试3次
154
- )
155
- logger.info(
156
- f"连接创建成功: {self._current_host}:{self.port}(版本: {target_version})")
157
- return conn
158
- except Exception as e:
159
- attempts += 1
160
- last_error = e
161
- logger.error(
162
- f"连接节点 {self._current_host}:{self.port} 失败({attempts}/{max_attempts}): {str(e)}",
163
- exc_info=True
164
- )
165
- await asyncio.sleep(min(5 * attempts, self.reconnect_interval))
166
-
167
- raise ConnectionError(
168
- f"所有节点创建连接失败(节点列表: {self.hosts})"
169
- ) from last_error
74
+ if self._is_shutdown:
75
+ return False
76
+ # 存活条件:未关闭 + 已初始化 + 连接有效 + 主通道有效
77
+ return self._initialized and self._connection is not None and not self._connection.is_closed and self._channel is not None and not self._channel.is_closed
170
78
 
171
- async def _init_channel_pool(self):
172
- """初始化通道池(加锁保证原子性,绑定当前连接)"""
79
+ async def _create_connection(self) -> AbstractRobustConnection:
80
+ """创建原生自动重连连接(仅创建一次,内部自动重试)"""
173
81
  async with self._lock:
174
82
  if self._is_shutdown:
175
- raise RuntimeError("通道池已关闭,无法初始化")
83
+ raise RuntimeError("客户端已关闭,无法创建连接")
176
84
 
177
- # 校验当前连接有效性
178
- if not self._connection or self._connection.is_closed:
179
- raise RuntimeError("无有效连接,无法初始化通道池")
180
-
181
- self._free_channels.clear()
182
- self._used_channels.clear()
183
-
184
- # 创建指定数量的通道(保留原生自动恢复)
185
- for i in range(self.channel_pool_size):
186
- try:
187
- channel = await self._connection.channel() # 新通道自带自动恢复
188
- await channel.set_qos(prefetch_count=self.prefetch_count)
189
- self._free_channels.append(channel)
190
- except Exception as e:
191
- logger.error(
192
- f"创建通道失败(第{i+1}个,连接版本: {self._connection_version}): {str(e)}",
193
- exc_info=True
194
- )
195
- continue
196
-
197
- logger.info(
198
- f"通道池初始化完成 - 连接: {self._current_host}:{self.port}(版本: {self._connection_version}), "
199
- f"可用通道数: {len(self._free_channels)}/{self.channel_pool_size}(均带自动恢复)"
200
- )
201
-
202
- async def _reconnect_if_needed(self) -> bool:
203
- """连接失效时重连(加锁保护,严格单连接+释放旧资源)"""
204
- # 快速判断,避免无效加锁
205
- if self._is_shutdown or self._reconnecting:
206
- return False
85
+ conn_url = f"amqp://{self.username}:{self.password}@{self._current_host}:{self.port}/{self.virtualhost}?name={self.app_name}&heartbeat={self.heartbeat}&reconnect_interval={self.reconnect_interval}&fail_fast=1"
86
+ logger.info(f"尝试创建原生自动重连连接: {self._current_host}:{self.port}")
207
87
 
208
- self._reconnecting = True
209
88
  try:
210
- logger.warning(f"连接失效(当前版本: {self._connection_version}),开始重连...")
211
-
212
- # 1. 强制释放所有旧资源(加锁保证原子性)
213
- await self._safe_close_old_resources()
214
-
215
- # 2. 递增连接版本号(加锁保证原子性,区分新旧连接)
216
- async with self._lock:
217
- self._connection_version += 1
218
- target_version = self._connection_version
219
-
220
- # 3. 创建新连接(保留原生自动恢复)
221
- new_conn = await self._create_single_connection()
222
-
223
- # 4. 绑定新连接(加锁保证原子性)
224
- async with self._lock:
225
- self._connection = new_conn
226
-
227
- # 5. 重新初始化通道池(新通道带自动恢复)
228
- await self._init_channel_pool()
229
-
230
- # 6. 恢复消费者通道(新通道带自动恢复)
231
- await self._restore_consumer_channels()
232
-
233
- logger.info(f"重连成功(新连接版本: {target_version}),所有通道均带自动恢复")
234
- async with self._lock:
235
- self._initialized = True # 重连成功后标记为已初始化
236
- return True
89
+ conn = await connect_robust(
90
+ conn_url,
91
+ timeout=self.connection_timeout,
92
+ )
93
+ logger.info(f"连接创建成功: {self._current_host}:{self.port}(原生自动重连已启用)")
94
+ return conn
237
95
  except Exception as e:
238
- logger.error(f"重连失败: {str(e)}", exc_info=True)
239
- async with self._lock:
240
- self._initialized = False
241
- return False
242
- finally:
243
- self._reconnecting = False
96
+ logger.error(f"连接创建失败: {str(e)}", exc_info=True)
97
+ raise ConnectionError(
98
+ f"无法连接RabbitMQ主机 {self._current_host}:{self.port}") from e
244
99
 
245
- async def _restore_consumer_channels(self):
246
- """重连后恢复消费者通道(加锁保证原子性,新通道带自动恢复)"""
100
+ async def _init_single_channel(self):
101
+ """初始化单通道(通道自带原生自动恢复)"""
247
102
  async with self._lock:
248
- if not self._consumer_channels or not self._connection or self._connection.is_closed:
249
- return
250
- logger.info(
251
- f"开始恢复 {len(self._consumer_channels)} 个消费者通道(连接版本: {self._connection_version})")
103
+ # 先判断是否关闭(优先级最高)
104
+ if self._is_shutdown:
105
+ raise RuntimeError("客户端已关闭,无法初始化通道")
106
+ # 再判断连接是否有效
107
+ if not self._connection or self._connection.is_closed:
108
+ raise RuntimeError("无有效连接,无法初始化通道")
252
109
 
253
- # 临时保存消费者配置(队列名、回调、auto_ack、参数)
254
- consumer_configs = list(self._consumer_channels.items())
255
- self._consumer_channels.clear()
110
+ # 清理旧通道(如果存在)
111
+ if self._channel and not self._channel.is_closed:
112
+ await self._channel.close()
256
113
 
257
- # 重新创建消费者(不加锁,避免阻塞其他操作)
258
- for queue_name, (_, _, callback, auto_ack, kwargs) in consumer_configs:
114
+ # 创建单通道并设置QOS
259
115
  try:
260
- await self.consume_queue(queue_name, callback, auto_ack, **kwargs)
116
+ self._channel = await self._connection.channel()
117
+ await self._channel.set_qos(prefetch_count=self.prefetch_count)
118
+ logger.info(f"单通道初始化完成(带原生自动恢复)")
261
119
  except Exception as e:
262
- logger.error(
263
- f"恢复消费者队列 {queue_name} 失败: {str(e)}", exc_info=True)
120
+ logger.error(f"创建单通道失败: {str(e)}", exc_info=True)
121
+ raise
264
122
 
265
- async def _clean_invalid_channels(self):
266
- """清理失效通道并补充(加锁保证原子性,仅处理当前连接)"""
123
+ async def _check_and_recover_channel(self) -> RobustChannel:
124
+ """检查并恢复通道(确保通道有效,所有状态判断加锁)"""
267
125
  async with self._lock:
268
- if self._is_shutdown or self._reconnecting:
269
- return
270
-
271
- # 1. 校验当前连接有效性
272
- current_valid = (
273
- self._connection is not None
274
- and not self._connection.is_closed
275
- and not self._reconnecting
276
- )
277
- if not current_valid:
278
- # 连接失效,触发重连(不加锁,避免死锁)
279
- asyncio.create_task(self._reconnect_if_needed())
280
- return
126
+ # 1. 先判断是否关闭(优先级最高)
127
+ if self._is_shutdown:
128
+ raise RuntimeError("客户端已关闭,无法获取通道")
129
+ # 2. 检查连接状态
130
+ if not self._connection or self._connection.is_closed:
131
+ raise RuntimeError("连接已关闭(等待原生重连)")
132
+ # 3. 通道失效时重新创建
133
+ if not self._channel or self._channel.is_closed:
134
+ logger.warning("通道失效,重新创建(依赖原生自动恢复)")
135
+ await self._init_single_channel()
281
136
 
282
- # 2. 清理空闲通道(仅保留当前连接的有效通道)
283
- valid_free = []
284
- for chan in self._free_channels:
285
- try:
286
- if not chan.is_closed and chan.connection == self._connection:
287
- valid_free.append(chan)
288
- else:
289
- logger.warning(f"清理失效空闲通道(连接版本不匹配或已关闭)")
290
- except Exception:
291
- logger.warning(f"清理异常空闲通道")
292
- self._free_channels = valid_free
293
-
294
- # 3. 清理使用中通道(仅保留当前连接的有效通道)
295
- valid_used = set()
296
- for chan in self._used_channels:
297
- try:
298
- if not chan.is_closed and chan.connection == self._connection:
299
- valid_used.add(chan)
300
- else:
301
- logger.warning(f"清理失效使用中通道(连接版本不匹配或已关闭)")
302
- except Exception:
303
- logger.warning(f"清理异常使用中通道")
304
- self._used_channels = valid_used
305
-
306
- # 4. 补充通道到指定大小(新通道带自动恢复)
307
- total_valid = len(self._free_channels) + len(self._used_channels)
308
- missing = self.channel_pool_size - total_valid
309
- if missing > 0:
310
- logger.info(
311
- f"通道池缺少{missing}个通道,补充中(连接版本: {self._connection_version})...")
312
- for _ in range(missing):
313
- try:
314
- channel = await self._connection.channel() # 新通道带自动恢复
315
- await channel.set_qos(prefetch_count=self.prefetch_count)
316
- self._free_channels.append(channel)
317
- except Exception as e:
318
- logger.error(f"补充通道失败: {str(e)}", exc_info=True)
319
- break
137
+ return self._channel
320
138
 
321
139
  async def init_pools(self):
322
- """初始化通道池(加锁保证原子性,仅执行一次)"""
140
+ """初始化客户端(仅执行一次)"""
323
141
  async with self._lock:
142
+ # 原子化判断:是否已关闭/已初始化
143
+ if self._is_shutdown:
144
+ raise RuntimeError("客户端已关闭,无法初始化")
324
145
  if self._initialized:
325
- logger.warning("通道池已初始化,无需重复调用")
146
+ logger.warning("客户端已初始化,无需重复调用")
326
147
  return
327
- if self._is_shutdown:
328
- raise RuntimeError("通道池已关闭,无法初始化")
329
148
 
330
149
  try:
331
- # 1. 创建新连接(保留原生自动恢复)
332
- new_conn = await self._create_single_connection()
333
-
334
- # 2. 初始化连接版本号和绑定连接(加锁保证原子性)
335
- async with self._lock:
336
- self._connection_version += 1
337
- self._connection = new_conn
150
+ # 1. 创建原生自动重连连接
151
+ self._connection = await self._create_connection()
338
152
 
339
- # 3. 初始化通道池(新通道带自动恢复)
340
- await self._init_channel_pool()
153
+ # 2. 初始化单通道
154
+ await self._init_single_channel()
341
155
 
342
- # 4. 标记为已初始化(加锁保证原子性)
156
+ # 3. 标记为已初始化(加锁保护)
343
157
  async with self._lock:
344
158
  self._initialized = True
345
159
 
346
- logger.info("RabbitMQ单连接通道池初始化完成(所有通道均带自动恢复)")
160
+ logger.info("RabbitMQ单通道客户端初始化完成(原生自动重连/恢复已启用)")
347
161
  except Exception as e:
348
162
  logger.error(f"初始化失败: {str(e)}", exc_info=True)
349
- await self._safe_close_old_resources()
163
+ await self.close() # 初始化失败直接关闭
350
164
  raise
351
165
 
352
- async def acquire_channel(self) -> Tuple[Channel, AbstractRobustConnection]:
353
- """获取通道(加锁保证原子性,返回当前连接+带自动恢复的通道)"""
354
- # 快速判断,避免无效加锁
166
+ async def acquire_channel(self) -> Tuple[RobustChannel, AbstractRobustConnection]:
167
+ """获取单通道(返回 (通道, 连接) 元组,保持API兼容)"""
355
168
  async with self._lock:
356
- if not self._initialized:
357
- raise RuntimeError("通道池未初始化,请先调用init_pools()")
169
+ # 原子化状态校验
358
170
  if self._is_shutdown:
359
- raise RuntimeError("通道池已关闭,无法获取通道")
360
-
361
- # 先清理失效通道(加锁保证原子性)
362
- await self._clean_invalid_channels()
363
-
364
- async with self._lock:
365
- # 双重校验连接有效性
366
- current_valid = (
367
- self._connection is not None
368
- and not self._connection.is_closed
369
- and not self._reconnecting
370
- )
371
- if not current_valid:
372
- # 连接失效,触发重连(不加锁,避免死锁)
373
- reconnect_success = await self._reconnect_if_needed()
374
- if not reconnect_success:
375
- raise RuntimeError("连接失效且重连失败,无法获取通道")
376
-
377
- # 优先从空闲池获取(带自动恢复的通道)
378
- if self._free_channels:
379
- channel = self._free_channels.pop()
380
- self._used_channels.add(channel)
381
- return channel, self._connection
382
-
383
- # 通道池已满,创建临时通道(带自动恢复,用完关闭)
384
- try:
385
- channel = await self._connection.channel() # 临时通道带自动恢复
386
- await channel.set_qos(prefetch_count=self.prefetch_count)
387
- self._used_channels.add(channel)
388
- logger.warning(
389
- f"通道池已达上限({self.channel_pool_size}),创建临时通道(带自动恢复,用完自动关闭)"
390
- )
391
- return channel, self._connection
392
- except Exception as e:
393
- logger.error(f"获取通道失败: {str(e)}", exc_info=True)
394
- raise
395
-
396
- async def release_channel(self, channel: Channel, conn: AbstractRobustConnection):
397
- """释放通道(加锁保证原子性,仅归还当前连接的有效通道)"""
398
- # 快速判断,避免无效加锁
399
- if not channel or not conn or self._is_shutdown:
400
- return
401
-
402
- async with self._lock:
403
- # 仅处理当前连接的通道(旧连接的通道直接关闭)
404
- if conn != self._connection:
405
- try:
406
- if not channel.is_closed:
407
- await channel.close()
408
- logger.warning(f"已关闭非当前连接的通道(版本不匹配,自动恢复终止)")
409
- except Exception as e:
410
- logger.warning(f"关闭非当前连接通道失败: {str(e)}")
411
- return
412
-
413
- # 通道不在使用中,直接返回
414
- if channel not in self._used_channels:
415
- return
416
-
417
- # 移除使用中标记
418
- self._used_channels.remove(channel)
171
+ raise RuntimeError("客户端已关闭,无法获取通道")
172
+ if not self._initialized:
173
+ raise RuntimeError("客户端未初始化,请先调用init_pools()")
419
174
 
420
- # 仅归还有效通道(当前连接有效+通道未关闭+池未满)
421
- current_valid = (
422
- self._connection is not None
423
- and not self._connection.is_closed
424
- and not self._reconnecting
425
- )
426
- if current_valid and not channel.is_closed and len(self._free_channels) < self.channel_pool_size:
427
- self._free_channels.append(channel)
428
- else:
429
- # 无效通道直接关闭(终止自动恢复)
430
- try:
431
- if not channel.is_closed:
432
- await channel.close()
433
- except Exception as e:
434
- logger.warning(f"关闭通道失败: {str(e)}")
175
+ # 检查并恢复通道
176
+ channel = await self._check_and_recover_channel()
177
+ return channel, self._connection # 单通道无需管理"使用中/空闲"状态
435
178
 
436
179
  async def declare_queue(self, queue_name: str, **kwargs) -> AbstractQueue:
437
- """声明队列(使用池内通道,带自动恢复)"""
438
- channel, conn = await self.acquire_channel()
439
- try:
440
- return await channel.declare_queue(queue_name, **kwargs)
441
- finally:
442
- await self.release_channel(channel, conn)
180
+ """声明队列(使用单通道)"""
181
+ channel, _ = await self.acquire_channel()
182
+ return await channel.declare_queue(queue_name, **kwargs)
443
183
 
444
184
  async def declare_exchange(self, exchange_name: str, exchange_type: str = "direct", **kwargs) -> AbstractExchange:
445
- """声明交换机(使用池内通道,带自动恢复)"""
446
- channel, conn = await self.acquire_channel()
447
- try:
448
- return await channel.declare_exchange(exchange_name, exchange_type, **kwargs)
449
- finally:
450
- await self.release_channel(channel, conn)
185
+ """声明交换机(使用单通道)"""
186
+ channel, _ = await self.acquire_channel()
187
+ return await channel.declare_exchange(exchange_name, exchange_type, **kwargs)
451
188
 
452
189
  async def publish_message(self, routing_key: str, message_body: bytes, exchange_name: str = "", **kwargs):
453
- """发布消息(使用池内通道,带自动恢复)"""
454
- channel, conn = await self.acquire_channel()
190
+ """发布消息(依赖原生自动重连/恢复)"""
191
+ channel, _ = await self.acquire_channel()
455
192
  try:
456
193
  exchange = channel.default_exchange if not exchange_name else await channel.get_exchange(exchange_name)
457
194
  message = Message(body=message_body, **kwargs)
458
195
  await exchange.publish(message, routing_key=routing_key)
459
196
  logger.debug(
460
- f"消息发布成功 - 连接: {self._current_host}:{self.port}(版本: {self._connection_version}), "
461
- f"交换机: {exchange.name}, 路由键: {routing_key}"
197
+ f"消息发布成功 - 交换机: {exchange.name}, 路由键: {routing_key}"
462
198
  )
463
199
  except Exception as e:
464
200
  logger.error(f"发布消息失败: {str(e)}", exc_info=True)
465
- # 发布失败触发重连(下次使用新通道)
466
- asyncio.create_task(self._reconnect_if_needed())
467
- raise
468
- finally:
469
- await self.release_channel(channel, conn)
201
+ raise # 原生会自动重连,无需手动处理
470
202
 
471
203
  async def consume_queue(self, queue_name: str, callback: Callable[[AbstractMessage], asyncio.Future], auto_ack: bool = False, **kwargs):
472
- """消费队列(使用独立通道,带自动恢复,支持多消费者)"""
473
- # 快速判断,避免无效加锁
204
+ """消费队列(独立通道,带原生自动恢复)"""
474
205
  async with self._lock:
475
- if not self._initialized:
476
- raise RuntimeError("通道池未初始化,请先调用init_pools()")
206
+ # 原子化状态校验
477
207
  if self._is_shutdown:
478
- raise RuntimeError("通道池已关闭,无法启动消费")
208
+ raise RuntimeError("客户端已关闭,无法启动消费")
209
+ if not self._initialized:
210
+ raise RuntimeError("客户端未初始化,请先调用init_pools()")
211
+ if queue_name in self._consumer_channels:
212
+ logger.warning(f"队列 {queue_name} 已在消费中,无需重复启动")
213
+ return
479
214
 
480
215
  # 先声明队列(确保队列存在)
481
216
  await self.declare_queue(queue_name, **kwargs)
482
217
 
483
- # 获取独立通道(消费者通道不放入普通池)
484
- channel, conn = await self.acquire_channel()
485
-
486
- # 注册消费者通道(加锁保证原子性)
218
+ # 创建独立的消费者通道(不使用主单通道,避免消费阻塞发布)
487
219
  async with self._lock:
220
+ if self._is_shutdown: # 二次校验:防止创建通道前客户端被关闭
221
+ raise RuntimeError("客户端已关闭,无法创建消费者通道")
222
+ if not self._connection or self._connection.is_closed:
223
+ raise RuntimeError("无有效连接,无法创建消费者通道")
224
+ channel = await self._connection.channel()
225
+ await channel.set_qos(prefetch_count=self.prefetch_count)
226
+
227
+ # 注册消费者通道
488
228
  self._consumer_channels[queue_name] = (
489
- channel, conn, callback, auto_ack, kwargs)
229
+ channel, callback, auto_ack, kwargs)
490
230
 
491
231
  async def consume_callback_wrapper(message: AbstractMessage):
492
- """消费回调包装(处理通道失效和自动恢复)"""
232
+ """消费回调包装(处理通道失效,依赖原生恢复)"""
493
233
  try:
494
- # 加锁校验通道和连接状态(原子性)
495
234
  async with self._lock:
496
- conn_matched = conn == self._connection
235
+ # 原子化校验状态:客户端是否关闭 + 通道是否有效 + 连接是否有效
236
+ if self._is_shutdown:
237
+ logger.warning(f"客户端已关闭,拒绝处理消息(队列: {queue_name})")
238
+ if not auto_ack:
239
+ await message.nack(requeue=True)
240
+ return
497
241
  channel_valid = not channel.is_closed
498
- current_conn_valid = (
499
- self._connection is not None
500
- and not self._connection.is_closed
501
- and not self._reconnecting
502
- )
503
-
504
- # 通道/连接失效,触发重连恢复
505
- if not conn_matched or not channel_valid or not current_conn_valid:
506
- logger.warning(
507
- f"消费者通道 {queue_name} 失效(连接版本不匹配/通道关闭),触发重连恢复")
508
-
509
- # 移除旧消费者记录(加锁保证原子性)
510
- async with self._lock:
511
- if self._consumer_channels.get(queue_name) == (channel, conn, callback, auto_ack, kwargs):
512
- del self._consumer_channels[queue_name]
513
-
514
- # 释放旧通道(加锁保证原子性)
515
- await self.release_channel(channel, conn)
516
-
517
- # 重新创建消费者(新通道带自动恢复)
518
- asyncio.create_task(self.consume_queue(
519
- queue_name, callback, auto_ack, **kwargs))
520
-
521
- # Nack消息(避免丢失)
242
+ conn_valid = self._connection and not self._connection.is_closed
243
+
244
+ if not channel_valid or not conn_valid:
245
+ logger.warning(f"消费者通道 {queue_name} 失效(等待原生自动恢复)")
522
246
  if not auto_ack:
523
247
  await message.nack(requeue=True)
524
248
  return
@@ -531,32 +255,20 @@ class RabbitMQConnectionPool:
531
255
  logger.error(f"消费者通道 {queue_name} 关闭: {str(e)}", exc_info=True)
532
256
  if not auto_ack:
533
257
  await message.nack(requeue=True)
534
- asyncio.create_task(self._reconnect_if_needed())
535
258
  except aiormq.exceptions.ChannelInvalidStateError as e:
536
259
  logger.error(
537
260
  f"消费者通道 {queue_name} 状态异常: {str(e)}", exc_info=True)
538
261
  if not auto_ack:
539
262
  await message.nack(requeue=True)
540
- asyncio.create_task(self.consume_queue(
541
- queue_name, callback, auto_ack, **kwargs))
542
263
  except Exception as e:
543
264
  logger.error(
544
265
  f"消费消息失败(队列: {queue_name}): {str(e)}", exc_info=True)
545
266
  if not auto_ack:
546
267
  await message.nack(requeue=True)
547
268
 
548
- # 日志输出(加锁获取当前连接信息)
549
- async with self._lock:
550
- current_host = self._current_host
551
- current_version = self._connection_version
552
-
553
- logger.info(
554
- f"开始消费队列: {queue_name} - 连接: {current_host}:{self.port}(版本: {current_version}), "
555
- f"通道带自动恢复"
556
- )
269
+ logger.info(f"开始消费队列: {queue_name}(通道带原生自动恢复)")
557
270
 
558
271
  try:
559
- # 启动消费(使用带自动恢复的通道)
560
272
  await channel.basic_consume(
561
273
  queue_name,
562
274
  consumer_callback=consume_callback_wrapper,
@@ -565,22 +277,62 @@ class RabbitMQConnectionPool:
565
277
  )
566
278
  except Exception as e:
567
279
  logger.error(f"启动消费失败(队列: {queue_name}): {str(e)}", exc_info=True)
568
- # 清理异常的消费者记录和通道(加锁保证原子性)
569
- await self.release_channel(channel, conn)
570
- async with self._lock:
571
- if self._consumer_channels.get(queue_name) == (channel, conn, callback, auto_ack, kwargs):
572
- del self._consumer_channels[queue_name]
280
+ # 清理异常资源
281
+ try:
282
+ async with self._lock:
283
+ if not channel.is_closed:
284
+ await channel.close()
285
+ # 移除无效的消费者通道注册
286
+ if queue_name in self._consumer_channels:
287
+ del self._consumer_channels[queue_name]
288
+ except Exception as close_e:
289
+ logger.warning(f"关闭消费者通道失败: {str(close_e)}")
573
290
  raise
574
291
 
575
292
  async def close(self):
576
- """关闭通道池(加锁保证原子性,释放所有资源)"""
293
+ """关闭客户端(释放所有资源,原子化状态管理)"""
577
294
  async with self._lock:
578
295
  if self._is_shutdown:
579
- logger.warning("通道池已关闭,无需重复操作")
296
+ logger.warning("客户端已关闭,无需重复操作")
580
297
  return
298
+ # 先标记为关闭,阻止后续所有操作(原子化修改)
581
299
  self._is_shutdown = True
300
+ self._initialized = False
301
+
302
+ logger.info("开始关闭RabbitMQ单通道客户端(释放所有资源)...")
303
+
304
+ # 1. 关闭所有消费者通道
305
+ consumer_channels = []
306
+ async with self._lock:
307
+ consumer_channels = list(self._consumer_channels.values())
308
+ self._consumer_channels.clear()
309
+ for channel, _, _, _ in consumer_channels:
310
+ try:
311
+ if not channel.is_closed:
312
+ await channel.close()
313
+ except Exception as e:
314
+ logger.warning(f"关闭消费者通道失败: {str(e)}")
315
+
316
+ # 2. 关闭主单通道
317
+ if self._channel:
318
+ try:
319
+ async with self._lock:
320
+ if not self._channel.is_closed:
321
+ await self._channel.close()
322
+ except Exception as e:
323
+ logger.warning(f"关闭主通道失败: {str(e)}")
324
+ self._channel = None
325
+
326
+ # 3. 关闭连接(终止原生自动重连)
327
+ if self._connection:
328
+ try:
329
+ async with self._lock:
330
+ if not self._connection.is_closed:
331
+ await self._connection.close()
332
+ logger.info(
333
+ f"已关闭连接: {self._current_host}:{self.port}(终止原生自动重连)")
334
+ except Exception as e:
335
+ logger.warning(f"关闭连接失败: {str(e)}")
336
+ self._connection = None
582
337
 
583
- logger.info("开始关闭RabbitMQ单连接通道池...")
584
- # 强制释放所有资源(包括自动恢复的通道)
585
- await self._safe_close_old_resources()
586
- logger.info("RabbitMQ单连接通道池已完全关闭(所有自动恢复逻辑已终止)")
338
+ logger.info("RabbitMQ单通道客户端已完全关闭")