sycommon-python-lib 0.1.38__py3-none-any.whl → 0.1.40__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
- from typing import Optional, List
1
+ import asyncio
2
+ from typing import List, Set
2
3
  from aio_pika import connect_robust, Channel
3
4
  from aio_pika.abc import AbstractRobustConnection
4
- from aio_pika.pool import Pool
5
5
 
6
6
  from sycommon.logging.kafka_log import SYLogger
7
7
 
@@ -9,7 +9,7 @@ logger = SYLogger
9
9
 
10
10
 
11
11
  class RabbitMQConnectionPool:
12
- """RabbitMQ连接池管理,负责创建和管理连接池与通道池"""
12
+ """RabbitMQ连接池管理(简化实现,避免上下文管理器冲突)"""
13
13
 
14
14
  def __init__(
15
15
  self,
@@ -33,72 +33,148 @@ class RabbitMQConnectionPool:
33
33
  self.app_name = app_name or "rabbitmq-client"
34
34
  self.heartbeat = heartbeat
35
35
 
36
- # 连接池和通道池
37
- self.connection_pool: Optional[Pool] = None
38
- self.channel_pool: Optional[Pool] = None
36
+ # 连接池配置
39
37
  self.connection_pool_size = connection_pool_size
40
38
  self.channel_pool_size = channel_pool_size
41
39
 
40
+ # 实际存储的连接和通道
41
+ self._connections: List[AbstractRobustConnection] = []
42
+ self._free_channels: List[Channel] = []
43
+ self._used_channels: Set[Channel] = set()
44
+
45
+ # 锁用于线程安全
46
+ self._conn_lock = asyncio.Lock()
47
+ self._chan_lock = asyncio.Lock()
48
+
49
+ # 连接状态
50
+ self._initialized = False
51
+
42
52
  async def init_pools(self):
43
- """初始化连接池和通道池"""
44
- # 连接创建函数(支持集群节点轮询)
45
- async def create_connection() -> AbstractRobustConnection:
46
- # 轮询选择主机(简单负载均衡)
47
- hosts = self.hosts.copy()
48
- while hosts:
49
- host = hosts.pop(0)
53
+ """初始化连接池(创建指定数量的连接)"""
54
+ if self._initialized:
55
+ logger.warning("连接池已初始化,无需重复调用")
56
+ return
57
+
58
+ try:
59
+ # 创建核心连接(数量=connection_pool_size)
60
+ for i in range(self.connection_pool_size):
61
+ conn = await self._create_connection()
62
+ self._connections.append(conn)
63
+ # 为每个连接创建初始通道(数量=channel_pool_size//connection_pool_size)
64
+ chan_count_per_conn = self.channel_pool_size // self.connection_pool_size
65
+ for _ in range(chan_count_per_conn):
66
+ chan = await conn.channel()
67
+ self._free_channels.append(chan)
68
+
69
+ self._initialized = True
70
+ logger.info(
71
+ f"RabbitMQ连接池初始化成功 - 连接数: {len(self._connections)}, "
72
+ f"空闲通道数: {len(self._free_channels)}, 集群节点: {self.hosts}"
73
+ )
74
+ except Exception as e:
75
+ logger.error(f"连接池初始化失败: {str(e)}", exc_info=True)
76
+ # 清理异常状态
77
+ await self.close()
78
+ raise
79
+
80
+ async def _create_connection(self) -> AbstractRobustConnection:
81
+ """创建单个RabbitMQ连接(支持集群节点轮询)"""
82
+ hosts = self.hosts.copy()
83
+ while hosts:
84
+ host = hosts.pop(0)
85
+ try:
86
+ connection = await connect_robust(
87
+ host=host,
88
+ port=self.port,
89
+ login=self.username,
90
+ password=self.password,
91
+ virtualhost=self.virtualhost,
92
+ heartbeat=self.heartbeat,
93
+ client_properties={
94
+ "connection_name": f"{self.app_name}@{host}"
95
+ },
96
+ reconnect_interval=2 # aio_pika 内置重连间隔
97
+ )
98
+ logger.info(f"成功连接到 RabbitMQ 节点: {host}:{self.port}")
99
+ return connection
100
+ except Exception as e:
101
+ logger.warning(
102
+ f"连接主机 {host}:{self.port} 失败,尝试下一个节点: {str(e)}")
103
+ if not hosts:
104
+ raise # 所有节点失败时抛出异常
105
+
106
+ async def acquire_channel(self) -> Channel:
107
+ """获取通道(从空闲通道池获取,无则创建新通道)"""
108
+ if not self._initialized:
109
+ raise RuntimeError("连接池未初始化,请先调用 init_pools()")
110
+
111
+ async with self._chan_lock:
112
+ # 优先从空闲通道池获取
113
+ if self._free_channels:
114
+ channel = self._free_channels.pop()
115
+ # 检查通道是否有效
116
+ if not channel.is_closed:
117
+ self._used_channels.add(channel)
118
+ return channel
119
+ else:
120
+ logger.warning("发现无效通道,已自动清理")
121
+
122
+ # 空闲通道不足,创建新通道(不超过最大限制)
123
+ if len(self._used_channels) < self.channel_pool_size:
124
+ # 选择一个空闲连接创建通道
125
+ async with self._conn_lock:
126
+ for conn in self._connections:
127
+ if not conn.is_closed:
128
+ try:
129
+ channel = await conn.channel()
130
+ self._used_channels.add(channel)
131
+ logger.info(
132
+ f"创建新通道,当前通道数: {len(self._used_channels)}/{self.channel_pool_size}")
133
+ return channel
134
+ except Exception as e:
135
+ logger.warning(f"使用连接创建通道失败: {str(e)}")
136
+ # 所有连接都无效,尝试重新创建连接
137
+ conn = await self._create_connection()
138
+ self._connections.append(conn)
139
+ channel = await conn.channel()
140
+ self._used_channels.add(channel)
141
+ return channel
142
+ else:
143
+ raise RuntimeError(f"通道池已达最大限制: {self.channel_pool_size}")
144
+
145
+ async def release_channel(self, channel: Channel):
146
+ """释放通道(归还到空闲通道池)"""
147
+ async with self._chan_lock:
148
+ if channel in self._used_channels:
149
+ self._used_channels.remove(channel)
150
+ # 通道有效则归还,无效则丢弃
151
+ if not channel.is_closed:
152
+ self._free_channels.append(channel)
153
+ else:
154
+ logger.warning("释放无效通道,已自动丢弃")
155
+
156
+ async def close(self):
157
+ """关闭连接池(释放所有连接和通道)"""
158
+ # 释放所有通道
159
+ async with self._chan_lock:
160
+ for channel in self._free_channels + list(self._used_channels):
50
161
  try:
51
- return await connect_robust(
52
- host=host,
53
- port=self.port,
54
- login=self.username,
55
- password=self.password,
56
- virtualhost=self.virtualhost,
57
- heartbeat=self.heartbeat,
58
- client_properties={
59
- "connection_name": f"{self.app_name}@{host}"
60
- }
61
- )
162
+ if not channel.is_closed:
163
+ await channel.close()
62
164
  except Exception as e:
63
- logger.warning(
64
- f"连接主机 {host}:{self.port} 失败,尝试下一个节点: {str(e)}")
65
- if not hosts:
66
- raise # 所有节点都失败时抛出异常
67
-
68
- # 初始化连接池
69
- self.connection_pool = Pool(
70
- create_connection,
71
- max_size=self.connection_pool_size
72
- )
73
-
74
- # 通道创建函数
75
- async def create_channel() -> Channel:
76
- async with self.connection_pool.acquire() as connection:
77
- channel = await connection.channel()
78
- return channel
79
-
80
- # 初始化通道池
81
- self.channel_pool = Pool(
82
- create_channel,
83
- max_size=self.channel_pool_size
84
- )
85
-
86
- logger.info(
87
- f"RabbitMQ连接池初始化完成 - 连接池大小: {self.connection_pool_size}, "
88
- f"通道池大小: {self.channel_pool_size}, 集群节点: {self.hosts}"
89
- )
165
+ logger.warning(f"关闭通道失败: {str(e)}")
166
+ self._free_channels.clear()
167
+ self._used_channels.clear()
90
168
 
91
- async def close(self):
92
- """关闭连接池和通道池"""
93
- if self.channel_pool:
94
- await self.channel_pool.close()
95
- if self.connection_pool:
96
- await self.connection_pool.close()
97
- logger.info("RabbitMQ连接池已关闭")
98
-
99
- async def __aenter__(self):
100
- await self.init_pools()
101
- return self
102
-
103
- async def __aexit__(self, exc_type, exc, tb):
104
- await self.close()
169
+ # 关闭所有连接
170
+ async with self._conn_lock:
171
+ for conn in self._connections:
172
+ try:
173
+ if not conn.is_closed:
174
+ await conn.close()
175
+ except Exception as e:
176
+ logger.warning(f"关闭连接失败: {str(e)}")
177
+ self._connections.clear()
178
+
179
+ self._initialized = False
180
+ logger.info("RabbitMQ连接池已完全关闭")
@@ -179,18 +179,14 @@ class RabbitMQService:
179
179
  auto_delete=kwargs.get('auto_delete', False),
180
180
  auto_parse_json=kwargs.get('auto_parse_json', True),
181
181
  create_if_not_exists=create_if_not_exists,
182
- connection_timeout=kwargs.get('connection_timeout', 10),
183
182
  rpc_timeout=kwargs.get('rpc_timeout', 10),
184
- reconnection_delay=kwargs.get('reconnection_delay', 1),
185
- max_reconnection_attempts=kwargs.get(
186
- 'max_reconnection_attempts', 5),
187
183
  prefetch_count=kwargs.get('prefetch_count', 2),
188
184
  consumption_stall_threshold=kwargs.get(
189
185
  'consumption_stall_threshold', 60), # 延长停滞阈值
190
186
  )
191
187
 
192
- # 使用declare_queue控制是否声明队列(发送器不声明,监听器声明)
193
- await client.connect(declare_queue=not is_sender)
188
+ # 新客户端connect无declare_queue参数,自动根据create_if_not_exists处理
189
+ await client.connect()
194
190
 
195
191
  # 监听器客户端连接后延迟1秒,确保消费状态就绪(仅首次启动)
196
192
  if not is_sender and create_if_not_exists:
@@ -245,13 +241,13 @@ class RabbitMQService:
245
241
  if not queue:
246
242
  logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
247
243
  client.create_if_not_exists = True
248
- await client.connect(force_reconnect=True, declare_queue=True)
244
+ await client.connect() # 移除force_reconnectdeclare_queue
249
245
  return client
250
246
  else:
251
247
  logger.info(f"客户端 '{client_name}' 连接已关闭,重新连接")
252
248
  if not is_sender:
253
249
  client.create_if_not_exists = True
254
- await client.connect(declare_queue=not is_sender)
250
+ await client.connect() # 移除force_reconnect和declare_queue
255
251
  return client
256
252
 
257
253
  # 创建新客户端
@@ -267,7 +263,7 @@ class RabbitMQService:
267
263
  app_name=cls._config.get("APP_NAME", ""),
268
264
  **kwargs
269
265
  )
270
- await client.connect(declare_queue=False)
266
+ await client.connect() # 移除declare_queue=False
271
267
  cls._clients[client_name] = client
272
268
  return client
273
269
 
@@ -280,7 +276,7 @@ class RabbitMQService:
280
276
  client = await cls._create_client(
281
277
  initial_queue_name, ** kwargs
282
278
  )
283
- await client.connect(declare_queue=True)
279
+ await client.connect() # 移除declare_queue=True
284
280
  cls._clients[client_name] = client
285
281
  return client
286
282
 
@@ -292,14 +288,14 @@ class RabbitMQService:
292
288
  )
293
289
 
294
290
  client.create_if_not_exists = True
295
- await client.connect(declare_queue=True)
291
+ await client.connect() # 移除declare_queue=True
296
292
 
297
293
  # 验证队列是否创建成功(异步获取队列)
298
294
  _, _, queue = await client._get_connection_resources()
299
295
  if not queue:
300
296
  logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
301
297
  client.create_if_not_exists = True
302
- await client.connect(force_reconnect=True, declare_queue=True)
298
+ await client.connect()
303
299
  _, _, queue = await client._get_connection_resources()
304
300
  if not queue:
305
301
  raise Exception(f"无法创建队列 '{initial_queue_name}'")
@@ -348,7 +344,7 @@ class RabbitMQService:
348
344
  if normalized_name in cls._clients:
349
345
  client = cls._clients[normalized_name]
350
346
  if not await client.is_connected:
351
- await client.connect(declare_queue=False)
347
+ await client.connect() # 移除declare_queue=False
352
348
  else:
353
349
  client = await cls.get_client(
354
350
  client_name=normalized_name,
@@ -535,15 +531,19 @@ class RabbitMQService:
535
531
  while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
536
532
  try:
537
533
  # 启动消费前再次校验连接和队列状态
538
- if not await client.is_connected:
534
+ if not await client.is_connected: # 确保这里没有括号
539
535
  logger.info(f"消费者 '{client_name}' 连接断开,尝试重连")
540
- await client.connect(force_reconnect=True, declare_queue=True)
536
+ await client.connect()
541
537
 
542
538
  # 确保队列已就绪
543
539
  _, _, queue = await client._get_connection_resources()
544
540
  if not queue:
545
541
  raise Exception("队列未初始化完成")
546
542
 
543
+ # 确保消息处理器已设置
544
+ if not hasattr(client, '_message_handler') or not client._message_handler:
545
+ raise Exception("消息处理器未设置")
546
+
547
547
  consumer_tag = await client.start_consuming()
548
548
  if consumer_tag:
549
549
  break
@@ -552,7 +552,7 @@ class RabbitMQService:
552
552
  logger.warning(
553
553
  f"启动消费者尝试 {attempt}/{max_attempts} 失败: {str(e)}")
554
554
  if attempt < max_attempts:
555
- await asyncio.sleep(1)
555
+ await asyncio.sleep(2) # 延长重试间隔
556
556
 
557
557
  if cls._is_shutdown:
558
558
  logger.info("服务关闭中,消费者启动中止")
@@ -644,7 +644,7 @@ class RabbitMQService:
644
644
  else:
645
645
  logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
646
646
  try:
647
- await client.connect(declare_queue=False)
647
+ await client.connect() # 移除declare_queue=False
648
648
  if await client.is_connected:
649
649
  return client
650
650
  except Exception as e:
@@ -662,7 +662,7 @@ class RabbitMQService:
662
662
  else:
663
663
  logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
664
664
  try:
665
- await client.connect(declare_queue=False)
665
+ await client.connect() # 移除declare_queue=False
666
666
  if await client.is_connected:
667
667
  return client
668
668
  except Exception as e:
@@ -698,7 +698,7 @@ class RabbitMQService:
698
698
  while retry_count < max_retry and not cls._is_shutdown:
699
699
  try:
700
700
  # 尝试重连,每次重试间隔1秒
701
- await sender.connect(force_reconnect=True, declare_queue=False)
701
+ await sender.connect() # 移除force_reconnectdeclare_queue
702
702
  if await sender.is_connected:
703
703
  logger.info(
704
704
  f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.38
3
+ Version: 0.1.40
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -35,9 +35,9 @@ sycommon/models/mqlistener_config.py,sha256=vXp2uMmd0XQ5B9noSRXWHewTy-juQ2y7IsWt
35
35
  sycommon/models/mqmsg_model.py,sha256=cxn0M5b0utQK6crMYmL-1waeGYHvK3AlGaRy23clqTE,277
36
36
  sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6D7I,268
37
37
  sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
38
- sycommon/rabbitmq/rabbitmq_client.py,sha256=eLfYuLSZuhMJ0Y-gn8dG1BBVvvKPrQLipAf1-Fub63I,42660
39
- sycommon/rabbitmq/rabbitmq_pool.py,sha256=_NMOO4CZy-I_anMqpzfYinz-8373_rg5FM9eqzdjGyU,3598
40
- sycommon/rabbitmq/rabbitmq_service.py,sha256=fylrwIBYJfR2D7o2k_WxAbK5ZNNFV__vMcLBrmXe8-k,36589
38
+ sycommon/rabbitmq/rabbitmq_client.py,sha256=vyyfIWJ0b7tXZSK1m8fKERW6D-LQCrDvmjKmsPvOG6M,12141
39
+ sycommon/rabbitmq/rabbitmq_pool.py,sha256=YmvvFO8xP2pNnwuyZ0vp28XnfTzPScIPnBMih34f7Wc,7390
40
+ sycommon/rabbitmq/rabbitmq_service.py,sha256=LXoL7zm-1_iTts6S9CreEMJ66ExwRm9y4zUmcOqI1YM,36597
41
41
  sycommon/sse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  sycommon/sse/event.py,sha256=k_rBJy23R7crtzQeetT0Q73D8o5-5p-eESGSs_BPOj0,2797
43
43
  sycommon/sse/sse.py,sha256=__CfWEcYxOxQ-HpLor4LTZ5hLWqw9-2X7CngqbVHsfw,10128
@@ -52,8 +52,8 @@ sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
53
53
  sycommon/tools/snowflake.py,sha256=DdEj3T5r5OEvikp3puxqmmmz6BrggxomoSlnsRFb5dM,1174
54
54
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
55
- sycommon_python_lib-0.1.38.dist-info/METADATA,sha256=ttyk1WD6XP7AfWf4usymgNcm0ILuA_T5gijUTf3FCJ4,7037
56
- sycommon_python_lib-0.1.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- sycommon_python_lib-0.1.38.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
58
- sycommon_python_lib-0.1.38.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
59
- sycommon_python_lib-0.1.38.dist-info/RECORD,,
55
+ sycommon_python_lib-0.1.40.dist-info/METADATA,sha256=-9GpbSobbSHJj66au2M-0ThPFN5ynZEALO69lvLOClY,7037
56
+ sycommon_python_lib-0.1.40.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ sycommon_python_lib-0.1.40.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
58
+ sycommon_python_lib-0.1.40.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
59
+ sycommon_python_lib-0.1.40.dist-info/RECORD,,