sycommon-python-lib 0.1.49__py3-none-any.whl → 0.1.51__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,7 @@
1
1
  import asyncio
2
2
  import random
3
- from typing import Optional, List, Set, Tuple, Dict, Callable
4
- from aio_pika import connect_robust, Channel, Message
3
+ from typing import Optional, List, Dict, Callable, Tuple
4
+ from aio_pika import connect_robust, RobustChannel, Message
5
5
  from aio_pika.abc import (
6
6
  AbstractRobustConnection, AbstractQueue, AbstractExchange, AbstractMessage
7
7
  )
@@ -14,7 +14,7 @@ logger = SYLogger
14
14
 
15
15
 
16
16
  class RabbitMQConnectionPool:
17
- """单连接RabbitMQ通道池(核心特性:依赖connect_robust原生自动重连/恢复 + 仅关闭时释放资源 + 全场景加锁)"""
17
+ """单连接单通道RabbitMQ客户端(核心特性:依赖connect_robust原生自动重连/恢复 + 仅关闭时释放资源)"""
18
18
 
19
19
  def __init__(
20
20
  self,
@@ -23,11 +23,10 @@ class RabbitMQConnectionPool:
23
23
  username: str,
24
24
  password: str,
25
25
  virtualhost: str = "/",
26
- channel_pool_size: int = 1,
27
26
  heartbeat: int = 30,
28
27
  app_name: str = "",
29
28
  connection_timeout: int = 30,
30
- reconnect_interval: int = 30,
29
+ reconnect_interval: int = 5,
31
30
  prefetch_count: int = 2,
32
31
  ):
33
32
  # 基础配置校验与初始化
@@ -44,20 +43,18 @@ class RabbitMQConnectionPool:
44
43
  self.connection_timeout = connection_timeout
45
44
  self.reconnect_interval = reconnect_interval
46
45
  self.prefetch_count = prefetch_count
47
- self.channel_pool_size = max(1, channel_pool_size) # 确保池大小不小于1
48
46
 
49
47
  # 初始化时随机选择一个主机地址(固定使用,依赖原生重连)
50
48
  self._current_host: str = random.choice(self.hosts)
51
49
  logger.info(
52
50
  f"随机选择RabbitMQ主机: {self._current_host}(依赖connect_robust原生自动重连/恢复)")
53
51
 
54
- # 核心资源(单连接+通道池,基于原生自动重连)
52
+ # 核心资源(单连接+单通道,基于原生自动重连)
55
53
  self._connection: Optional[AbstractRobustConnection] = None # 原生自动重连连接
56
- self._free_channels: List[Channel] = [] # 空闲通道(原生自动恢复)
57
- self._used_channels: Set[Channel] = set() # 使用中通道(原生自动恢复)
58
- # 消费者通道跟踪
54
+ self._channel: Optional[RobustChannel] = None # 单通道(原生自动恢复)
55
+ # 消费者通道跟踪(独立于主通道)
59
56
  self._consumer_channels: Dict[str,
60
- Tuple[Channel, Callable, bool, dict]] = {}
57
+ Tuple[RobustChannel, Callable, bool, dict]] = {}
61
58
 
62
59
  # 状态控制(并发安全+生命周期管理)
63
60
  self._lock = asyncio.Lock()
@@ -65,41 +62,33 @@ class RabbitMQConnectionPool:
65
62
  self._is_shutdown = False
66
63
 
67
64
  async def _is_connection_valid(self) -> bool:
68
- """原子化检查连接有效性(仅判断是否初始化完成且未关闭)"""
65
+ """原子化检查连接有效性(所有状态判断均加锁,确保原子性)"""
69
66
  async with self._lock:
70
- return (
71
- self._initialized
72
- and self._connection is not None
73
- and not self._connection.is_closed
74
- and not self._is_shutdown
75
- )
67
+ # 优先级:先判断是否关闭,再判断是否初始化,最后判断连接状态
68
+ return not self._is_shutdown and self._initialized and self._connection is not None and not self._connection.is_closed
76
69
 
77
70
  @property
78
71
  async def is_alive(self) -> bool:
79
- """对外暴露的连接存活状态"""
80
- if self._is_shutdown:
81
- return False
82
- return await self._is_connection_valid()
72
+ """对外暴露的连接存活状态(原子化判断)"""
73
+ async with self._lock:
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
83
78
 
84
79
  async def _create_connection(self) -> AbstractRobustConnection:
85
80
  """创建原生自动重连连接(仅创建一次,内部自动重试)"""
86
- if self._is_shutdown:
87
- raise RuntimeError("通道池已关闭,无法创建连接")
81
+ async with self._lock:
82
+ if self._is_shutdown:
83
+ raise RuntimeError("客户端已关闭,无法创建连接")
88
84
 
89
- conn_url = f"amqp://{self.username}:{self.password}@{self._current_host}:{self.port}/{self.virtualhost}"
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"
90
86
  logger.info(f"尝试创建原生自动重连连接: {self._current_host}:{self.port}")
91
87
 
92
88
  try:
93
89
  conn = await connect_robust(
94
90
  conn_url,
95
- properties={
96
- "connection_name": f"{self.app_name}_conn",
97
- "product": self.app_name
98
- },
99
- heartbeat=self.heartbeat,
100
91
  timeout=self.connection_timeout,
101
- reconnect_interval=self.reconnect_interval, # 原生重连间隔
102
- max_reconnect_attempts=None, # 无限重试(按需调整)
103
92
  )
104
93
  logger.info(f"连接创建成功: {self._current_host}:{self.port}(原生自动重连已启用)")
105
94
  return conn
@@ -108,182 +97,98 @@ class RabbitMQConnectionPool:
108
97
  raise ConnectionError(
109
98
  f"无法连接RabbitMQ主机 {self._current_host}:{self.port}") from e
110
99
 
111
- async def _init_channel_pool(self):
112
- """初始化通道池(通道自带原生自动恢复)"""
100
+ async def _init_single_channel(self):
101
+ """初始化单通道(通道自带原生自动恢复)"""
113
102
  async with self._lock:
103
+ # 先判断是否关闭(优先级最高)
114
104
  if self._is_shutdown:
115
- raise RuntimeError("通道池已关闭,无法初始化")
105
+ raise RuntimeError("客户端已关闭,无法初始化通道")
106
+ # 再判断连接是否有效
116
107
  if not self._connection or self._connection.is_closed:
117
- raise RuntimeError("无有效连接,无法初始化通道池")
118
-
119
- # 清理无效通道(初始化前确保池为空)
120
- self._free_channels.clear()
121
- self._used_channels.clear()
122
-
123
- # 创建指定数量的通道
124
- for i in range(self.channel_pool_size):
125
- try:
126
- channel = await self._connection.channel() # 通道原生自动恢复
127
- await channel.set_qos(prefetch_count=self.prefetch_count)
128
- self._free_channels.append(channel)
129
- except Exception as e:
130
- logger.error(f"创建通道失败(第{i+1}个): {str(e)}", exc_info=True)
131
- continue
132
-
133
- logger.info(
134
- f"通道池初始化完成 - 可用通道数: {len(self._free_channels)}/{self.channel_pool_size} "
135
- f"(均带原生自动恢复)"
136
- )
108
+ raise RuntimeError("无有效连接,无法初始化通道")
109
+
110
+ # 清理旧通道(如果存在)
111
+ if self._channel and not self._channel.is_closed:
112
+ await self._channel.close()
137
113
 
138
- async def _clean_invalid_channels(self):
139
- """清理失效通道并补充(仅处理通道级失效,依赖原生恢复)"""
114
+ # 创建单通道并设置QOS
115
+ try:
116
+ self._channel = await self._connection.channel()
117
+ await self._channel.set_qos(prefetch_count=self.prefetch_count)
118
+ logger.info(f"单通道初始化完成(带原生自动恢复)")
119
+ except Exception as e:
120
+ logger.error(f"创建单通道失败: {str(e)}", exc_info=True)
121
+ raise
122
+
123
+ async def _check_and_recover_channel(self) -> RobustChannel:
124
+ """检查并恢复通道(确保通道有效,所有状态判断加锁)"""
140
125
  async with self._lock:
141
- if self._is_shutdown or not self._connection:
142
- 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()
143
136
 
144
- # 1. 清理空闲通道(保留有效通道)
145
- valid_free = []
146
- for chan in self._free_channels:
147
- try:
148
- if not chan.is_closed:
149
- valid_free.append(chan)
150
- else:
151
- logger.warning(f"清理失效空闲通道(将自动补充)")
152
- except Exception:
153
- logger.warning(f"清理异常空闲通道")
154
- self._free_channels = valid_free
155
-
156
- # 2. 清理使用中通道(保留有效通道)
157
- valid_used = set()
158
- for chan in self._used_channels:
159
- try:
160
- if not chan.is_closed:
161
- valid_used.add(chan)
162
- else:
163
- logger.warning(f"清理失效使用中通道")
164
- except Exception:
165
- logger.warning(f"清理异常使用中通道")
166
- self._used_channels = valid_used
167
-
168
- # 3. 补充缺失的通道(新通道带原生自动恢复)
169
- total_valid = len(self._free_channels) + len(self._used_channels)
170
- missing = self.channel_pool_size - total_valid
171
- if missing > 0:
172
- logger.info(f"通道池缺少{missing}个通道,补充中...")
173
- for _ in range(missing):
174
- try:
175
- channel = await self._connection.channel()
176
- await channel.set_qos(prefetch_count=self.prefetch_count)
177
- self._free_channels.append(channel)
178
- except Exception as e:
179
- logger.error(f"补充通道失败: {str(e)}", exc_info=True)
180
- break
137
+ return self._channel
181
138
 
182
139
  async def init_pools(self):
183
- """初始化通道池(仅执行一次)"""
140
+ """初始化客户端(仅执行一次)"""
184
141
  async with self._lock:
142
+ # 原子化判断:是否已关闭/已初始化
143
+ if self._is_shutdown:
144
+ raise RuntimeError("客户端已关闭,无法初始化")
185
145
  if self._initialized:
186
- logger.warning("通道池已初始化,无需重复调用")
146
+ logger.warning("客户端已初始化,无需重复调用")
187
147
  return
188
- if self._is_shutdown:
189
- raise RuntimeError("通道池已关闭,无法初始化")
190
148
 
191
149
  try:
192
150
  # 1. 创建原生自动重连连接
193
151
  self._connection = await self._create_connection()
194
152
 
195
- # 2. 初始化通道池
196
- await self._init_channel_pool()
153
+ # 2. 初始化单通道
154
+ await self._init_single_channel()
197
155
 
198
- # 3. 标记为已初始化
156
+ # 3. 标记为已初始化(加锁保护)
199
157
  async with self._lock:
200
158
  self._initialized = True
201
159
 
202
- logger.info("RabbitMQ通道池初始化完成(原生自动重连/恢复已启用)")
160
+ logger.info("RabbitMQ单通道客户端初始化完成(原生自动重连/恢复已启用)")
203
161
  except Exception as e:
204
162
  logger.error(f"初始化失败: {str(e)}", exc_info=True)
205
163
  await self.close() # 初始化失败直接关闭
206
164
  raise
207
165
 
208
- async def acquire_channel(self) -> Tuple[Channel, AbstractRobustConnection]:
209
- """获取通道(返回 (通道, 连接) 元组,保持API兼容)"""
210
- # 快速校验状态
166
+ async def acquire_channel(self) -> Tuple[RobustChannel, AbstractRobustConnection]:
167
+ """获取单通道(返回 (通道, 连接) 元组,保持API兼容)"""
211
168
  async with self._lock:
212
- if not self._initialized:
213
- raise RuntimeError("通道池未初始化,请先调用init_pools()")
169
+ # 原子化状态校验
214
170
  if self._is_shutdown:
215
- raise RuntimeError("通道池已关闭,无法获取通道")
216
- if not self._connection or self._connection.is_closed:
217
- raise RuntimeError("连接已关闭(等待原生重连)")
218
-
219
- # 清理失效通道
220
- await self._clean_invalid_channels()
221
-
222
- async with self._lock:
223
- # 优先从空闲池获取
224
- if self._free_channels:
225
- channel = self._free_channels.pop()
226
- self._used_channels.add(channel)
227
- return channel, self._connection # 返回 (通道, 连接) 元组
228
-
229
- # 池满时创建临时通道(用完自动关闭)
230
- try:
231
- channel = await self._connection.channel()
232
- await channel.set_qos(prefetch_count=self.prefetch_count)
233
- self._used_channels.add(channel)
234
- logger.warning(
235
- f"通道池已达上限({self.channel_pool_size}),创建临时通道(用完自动关闭)"
236
- )
237
- return channel, self._connection # 返回 (通道, 连接) 元组
238
- except Exception as e:
239
- logger.error(f"获取通道失败: {str(e)}", exc_info=True)
240
- raise
171
+ raise RuntimeError("客户端已关闭,无法获取通道")
172
+ if not self._initialized:
173
+ raise RuntimeError("客户端未初始化,请先调用init_pools()")
241
174
 
242
- async def release_channel(self, channel: Channel, conn: AbstractRobustConnection):
243
- """释放通道(接收通道和连接参数,保持API兼容)"""
244
- if not channel or not conn or self._is_shutdown:
245
- return
246
-
247
- async with self._lock:
248
- # 通道不在使用中,直接返回
249
- if channel not in self._used_channels:
250
- return
251
-
252
- # 移除使用中标记
253
- self._used_channels.remove(channel)
254
-
255
- # 仅归还有效通道(通道未关闭+池未满+连接匹配)
256
- if (not channel.is_closed
257
- and len(self._free_channels) < self.channel_pool_size
258
- and conn == self._connection): # 确保是当前连接的通道
259
- self._free_channels.append(channel)
260
- else:
261
- # 无效通道直接关闭(临时通道、池满或连接不匹配时)
262
- try:
263
- if not channel.is_closed:
264
- await channel.close()
265
- except Exception as e:
266
- logger.warning(f"关闭通道失败: {str(e)}")
175
+ # 检查并恢复通道
176
+ channel = await self._check_and_recover_channel()
177
+ return channel, self._connection # 单通道无需管理"使用中/空闲"状态
267
178
 
268
179
  async def declare_queue(self, queue_name: str, **kwargs) -> AbstractQueue:
269
- """声明队列(使用池内通道)"""
270
- channel, conn = await self.acquire_channel()
271
- try:
272
- return await channel.declare_queue(queue_name, **kwargs)
273
- finally:
274
- await self.release_channel(channel, conn)
180
+ """声明队列(使用单通道)"""
181
+ channel, _ = await self.acquire_channel()
182
+ return await channel.declare_queue(queue_name, **kwargs)
275
183
 
276
184
  async def declare_exchange(self, exchange_name: str, exchange_type: str = "direct", **kwargs) -> AbstractExchange:
277
- """声明交换机(使用池内通道)"""
278
- channel, conn = await self.acquire_channel()
279
- try:
280
- return await channel.declare_exchange(exchange_name, exchange_type, **kwargs)
281
- finally:
282
- await self.release_channel(channel, conn)
185
+ """声明交换机(使用单通道)"""
186
+ channel, _ = await self.acquire_channel()
187
+ return await channel.declare_exchange(exchange_name, exchange_type, **kwargs)
283
188
 
284
189
  async def publish_message(self, routing_key: str, message_body: bytes, exchange_name: str = "", **kwargs):
285
190
  """发布消息(依赖原生自动重连/恢复)"""
286
- channel, conn = await self.acquire_channel()
191
+ channel, _ = await self.acquire_channel()
287
192
  try:
288
193
  exchange = channel.default_exchange if not exchange_name else await channel.get_exchange(exchange_name)
289
194
  message = Message(body=message_body, **kwargs)
@@ -294,39 +199,49 @@ class RabbitMQConnectionPool:
294
199
  except Exception as e:
295
200
  logger.error(f"发布消息失败: {str(e)}", exc_info=True)
296
201
  raise # 原生会自动重连,无需手动处理
297
- finally:
298
- await self.release_channel(channel, conn)
299
202
 
300
203
  async def consume_queue(self, queue_name: str, callback: Callable[[AbstractMessage], asyncio.Future], auto_ack: bool = False, **kwargs):
301
204
  """消费队列(独立通道,带原生自动恢复)"""
302
- # 快速校验状态
303
205
  async with self._lock:
304
- if not self._initialized:
305
- raise RuntimeError("通道池未初始化,请先调用init_pools()")
206
+ # 原子化状态校验
306
207
  if self._is_shutdown:
307
- 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
308
214
 
309
215
  # 先声明队列(确保队列存在)
310
216
  await self.declare_queue(queue_name, **kwargs)
311
217
 
312
- # 获取独立通道(不放入普通池)
313
- channel, conn = await self.acquire_channel()
314
-
315
- # 注册消费者通道
218
+ # 创建独立的消费者通道(不使用主单通道,避免消费阻塞发布)
316
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
+ # 注册消费者通道
317
228
  self._consumer_channels[queue_name] = (
318
229
  channel, callback, auto_ack, kwargs)
319
230
 
320
231
  async def consume_callback_wrapper(message: AbstractMessage):
321
232
  """消费回调包装(处理通道失效,依赖原生恢复)"""
322
233
  try:
323
- # 校验通道和连接状态
324
234
  async with self._lock:
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
325
241
  channel_valid = not channel.is_closed
326
242
  conn_valid = self._connection and not self._connection.is_closed
327
- conn_matched = conn == self._connection
328
243
 
329
- if not channel_valid or not conn_valid or not conn_matched:
244
+ if not channel_valid or not conn_valid:
330
245
  logger.warning(f"消费者通道 {queue_name} 失效(等待原生自动恢复)")
331
246
  if not auto_ack:
332
247
  await message.nack(requeue=True)
@@ -363,24 +278,31 @@ class RabbitMQConnectionPool:
363
278
  except Exception as e:
364
279
  logger.error(f"启动消费失败(队列: {queue_name}): {str(e)}", exc_info=True)
365
280
  # 清理异常资源
366
- await self.release_channel(channel, conn)
367
- async with self._lock:
368
- if self._consumer_channels.get(queue_name) == (channel, callback, auto_ack, kwargs):
369
- del self._consumer_channels[queue_name]
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)}")
370
290
  raise
371
291
 
372
292
  async def close(self):
373
- """关闭通道池(仅主动关闭时释放所有资源)"""
293
+ """关闭客户端(释放所有资源,原子化状态管理)"""
374
294
  async with self._lock:
375
295
  if self._is_shutdown:
376
- logger.warning("通道池已关闭,无需重复操作")
296
+ logger.warning("客户端已关闭,无需重复操作")
377
297
  return
298
+ # 先标记为关闭,阻止后续所有操作(原子化修改)
378
299
  self._is_shutdown = True
379
300
  self._initialized = False
380
301
 
381
- logger.info("开始关闭RabbitMQ通道池(释放所有资源)...")
302
+ logger.info("开始关闭RabbitMQ单通道客户端(释放所有资源)...")
382
303
 
383
304
  # 1. 关闭所有消费者通道
305
+ consumer_channels = []
384
306
  async with self._lock:
385
307
  consumer_channels = list(self._consumer_channels.values())
386
308
  self._consumer_channels.clear()
@@ -391,27 +313,26 @@ class RabbitMQConnectionPool:
391
313
  except Exception as e:
392
314
  logger.warning(f"关闭消费者通道失败: {str(e)}")
393
315
 
394
- # 2. 关闭所有普通通道
395
- async with self._lock:
396
- all_channels = self._free_channels + list(self._used_channels)
397
- self._free_channels.clear()
398
- self._used_channels.clear()
399
- for channel in all_channels:
316
+ # 2. 关闭主单通道
317
+ if self._channel:
400
318
  try:
401
- if not channel.is_closed:
402
- await channel.close()
319
+ async with self._lock:
320
+ if not self._channel.is_closed:
321
+ await self._channel.close()
403
322
  except Exception as e:
404
- logger.warning(f"关闭通道失败: {str(e)}")
323
+ logger.warning(f"关闭主通道失败: {str(e)}")
324
+ self._channel = None
405
325
 
406
326
  # 3. 关闭连接(终止原生自动重连)
407
327
  if self._connection:
408
328
  try:
409
- if not self._connection.is_closed:
410
- await self._connection.close()
329
+ async with self._lock:
330
+ if not self._connection.is_closed:
331
+ await self._connection.close()
411
332
  logger.info(
412
333
  f"已关闭连接: {self._current_host}:{self.port}(终止原生自动重连)")
413
334
  except Exception as e:
414
335
  logger.warning(f"关闭连接失败: {str(e)}")
415
336
  self._connection = None
416
337
 
417
- logger.info("RabbitMQ通道池已完全关闭")
338
+ logger.info("RabbitMQ单通道客户端已完全关闭")