sycommon-python-lib 0.1.16__py3-none-any.whl → 0.1.18__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.

Potentially problematic release.


This version of sycommon-python-lib might be problematic. Click here for more details.

@@ -1,20 +1,19 @@
1
1
  import asyncio
2
- import logging
3
2
  import json
4
- from typing import Callable, Coroutine, Optional, Dict, Any, Union, Set, List
5
- from aio_pika import connect_robust, Message, DeliveryMode, ExchangeType
3
+ from typing import Callable, Coroutine, Optional, Dict, Any, Union, Set
4
+ from aio_pika import Message, DeliveryMode, ExchangeType
6
5
  from aio_pika.abc import (
7
- AbstractConnection,
8
6
  AbstractChannel,
9
7
  AbstractExchange,
10
8
  AbstractQueue,
11
9
  AbstractIncomingMessage,
12
- ConsumerTag
10
+ ConsumerTag,
13
11
  )
14
12
  from aiormq.exceptions import ChannelInvalidStateError, ConnectionClosed
15
13
 
16
14
  from sycommon.logging.kafka_log import SYLogger
17
15
  from sycommon.models.mqmsg_model import MQMsgModel
16
+ from sycommon.rabbitmq.rabbitmq_pool import RabbitMQConnectionPool
18
17
 
19
18
  # 最大重试次数限制
20
19
  MAX_RETRY_COUNT = 3
@@ -24,17 +23,13 @@ logger = SYLogger
24
23
 
25
24
  class RabbitMQClient:
26
25
  """
27
- RabbitMQ客户端,支持集群多节点配置,基于aio-pika实现
26
+ RabbitMQ客户端(基于连接池),支持集群多节点配置
28
27
  提供自动故障转移、连接恢复和消息可靠性保障
29
28
  """
30
29
 
31
30
  def __init__(
32
31
  self,
33
- hosts: List[str],
34
- port: int,
35
- username: str,
36
- password: str,
37
- virtualhost: str = "/",
32
+ connection_pool: RabbitMQConnectionPool,
38
33
  exchange_name: str = "system.topic.exchange",
39
34
  exchange_type: str = "topic",
40
35
  queue_name: Optional[str] = None,
@@ -45,47 +40,18 @@ class RabbitMQClient:
45
40
  create_if_not_exists: bool = True,
46
41
  connection_timeout: int = 10,
47
42
  rpc_timeout: int = 10,
48
- app_name: str = "",
49
43
  reconnection_delay: int = 1,
50
44
  max_reconnection_attempts: int = 5,
51
- heartbeat: int = 10,
52
45
  prefetch_count: int = 2,
53
46
  consumption_stall_threshold: int = 10
54
47
  ):
55
48
  """
56
- 初始化RabbitMQ客户端,支持集群多节点配置
57
-
58
- :param hosts: RabbitMQ主机地址列表(集群节点)
59
- :param port: RabbitMQ端口
60
- :param username: 用户名
61
- :param password: 密码
62
- :param virtualhost: 虚拟主机
63
- :param exchange_name: 交换机名称
64
- :param exchange_type: 交换机类型
65
- :param queue_name: 队列名称
66
- :param routing_key: 路由键
67
- :param durable: 是否持久化
68
- :param auto_delete: 是否自动删除
69
- :param auto_parse_json: 是否自动解析JSON消息
70
- :param create_if_not_exists: 如果资源不存在是否创建
71
- :param connection_timeout: 连接超时时间(秒)
72
- :param rpc_timeout: RPC操作超时时间(秒)
73
- :param app_name: 应用名称,用于标识连接
74
- :param reconnection_delay: 重连延迟(秒)
75
- :param max_reconnection_attempts: 最大重连尝试次数
76
- :param heartbeat: 心跳间隔(秒)
77
- :param prefetch_count: 预取消息数量
78
- :param consumption_stall_threshold: 消费停滞检测阈值(秒)
49
+ 初始化RabbitMQ客户端(依赖连接池)
50
+
51
+ :param connection_pool: 连接池实例
79
52
  """
80
- # 连接参数 - 支持多主机
81
- self.hosts = [host.strip() for host in hosts if host.strip()]
82
- if not self.hosts:
83
- raise ValueError("至少需要提供一个RabbitMQ主机地址")
84
- self.port = port
85
- self.username = username
86
- self.password = password
87
- self.virtualhost = virtualhost
88
- self.app_name = app_name or "rabbitmq-client"
53
+ # 连接池依赖
54
+ self.connection_pool = connection_pool
89
55
 
90
56
  # 交换器和队列参数
91
57
  self.exchange_name = exchange_name
@@ -102,23 +68,18 @@ class RabbitMQClient:
102
68
  self.rpc_timeout = rpc_timeout
103
69
  self.prefetch_count = prefetch_count
104
70
 
105
- # 重连和保活参数
71
+ # 重连参数
106
72
  self.reconnection_delay = reconnection_delay
107
73
  self.max_reconnection_attempts = max_reconnection_attempts
108
- self.heartbeat = heartbeat
109
74
 
110
75
  # 消息处理参数
111
76
  self.consumption_stall_threshold = consumption_stall_threshold
112
77
 
113
- # 连接和通道对象
114
- self.connection: Optional[AbstractConnection] = None
78
+ # 通道和资源对象(从池获取)
115
79
  self.channel: Optional[AbstractChannel] = None
116
80
  self.exchange: Optional[AbstractExchange] = None
117
81
  self.queue: Optional[AbstractQueue] = None
118
82
 
119
- # 当前活跃连接的主机
120
- self._active_host: Optional[str] = None
121
-
122
83
  # 状态跟踪
123
84
  self.actual_queue_name: Optional[str] = None
124
85
  self._exchange_exists = False
@@ -145,10 +106,8 @@ class RabbitMQClient:
145
106
 
146
107
  @property
147
108
  def is_connected(self) -> bool:
148
- """检查连接是否有效"""
109
+ """检查当前通道是否有效"""
149
110
  return (not self._closed and
150
- self.connection is not None and
151
- not self.connection.is_closed and
152
111
  self.channel is not None and
153
112
  not self.channel.is_closed and
154
113
  self.exchange is not None)
@@ -161,128 +120,78 @@ class RabbitMQClient:
161
120
  """更新最后消息处理时间戳"""
162
121
  self._last_message_processed = asyncio.get_event_loop().time()
163
122
 
164
- async def _check_exchange_exists(self) -> bool:
165
- """检查交换机是否存在"""
166
- if not self.channel:
167
- return False
123
+ async def _get_channel(self) -> AbstractChannel:
124
+ """从通道池获取通道(使用上下文管理器)"""
125
+ if not self.connection_pool.channel_pool:
126
+ raise Exception("连接池未初始化,请先调用init_pools")
127
+
128
+ # 使用async with获取通道,并通过变量返回
129
+ async with self.connection_pool.channel_pool.acquire() as channel:
130
+ return channel
168
131
 
132
+ async def _check_exchange_exists(self, channel: AbstractChannel) -> bool:
133
+ """检查交换机是否存在"""
169
134
  try:
170
- # 使用被动模式检查交换机是否存在
135
+ # 使用被动模式检查交换机
171
136
  await asyncio.wait_for(
172
- self.channel.declare_exchange(
137
+ channel.declare_exchange(
173
138
  name=self.exchange_name,
174
139
  type=self.exchange_type,
175
140
  passive=True
176
141
  ),
177
142
  timeout=self.rpc_timeout
178
143
  )
179
- self._exchange_exists = True
180
- self._update_activity_timestamp()
181
144
  return True
182
- except asyncio.TimeoutError:
183
- logger.error(
184
- f"检查交换机 '{self.exchange_name}' 超时 (主机: {self._active_host})")
185
- return False
186
- except Exception as e:
187
- logger.debug(
188
- f"交换机 '{self.exchange_name}' 不存在: {str(e)} (主机: {self._active_host})")
145
+ except Exception:
189
146
  return False
190
147
 
191
- async def _check_queue_exists(self) -> bool:
148
+ async def _check_queue_exists(self, channel: AbstractChannel) -> bool:
192
149
  """检查队列是否存在"""
193
- if not self.channel or not self.queue_name:
150
+ if not self.queue_name:
194
151
  return False
195
-
196
152
  try:
197
- # 使用被动模式检查队列是否存在
153
+ # 使用被动模式检查队列
198
154
  await asyncio.wait_for(
199
- self.channel.declare_queue(
155
+ channel.declare_queue(
200
156
  name=self.queue_name,
201
157
  passive=True
202
158
  ),
203
159
  timeout=self.rpc_timeout
204
160
  )
205
- self._queue_exists = True
206
- self._update_activity_timestamp()
207
161
  return True
208
- except asyncio.TimeoutError:
209
- logger.error(
210
- f"检查队列 '{self.queue_name}' 超时 (主机: {self._active_host})")
211
- return False
212
- except Exception as e:
213
- logger.debug(
214
- f"队列 '{self.queue_name}' 不存在: {str(e)} (主机: {self._active_host})")
162
+ except Exception:
215
163
  return False
216
164
 
217
- async def _bind_queue(self) -> bool:
165
+ async def _bind_queue(self, channel: AbstractChannel, queue: AbstractQueue, exchange: AbstractExchange) -> bool:
218
166
  """将队列绑定到交换机"""
219
- if not self.channel or not self.queue or not self.exchange:
220
- return False
221
-
222
- retries = 2
223
167
  bind_routing_key = self.routing_key if self.routing_key else '#'
224
168
 
225
- for attempt in range(retries + 1):
169
+ for attempt in range(MAX_RETRY_COUNT + 1):
226
170
  try:
227
171
  await asyncio.wait_for(
228
- self.queue.bind(
229
- self.exchange,
172
+ queue.bind(
173
+ exchange,
230
174
  routing_key=bind_routing_key
231
175
  ),
232
176
  timeout=self.rpc_timeout
233
177
  )
234
- self._queue_bound = True
235
- self._update_activity_timestamp()
236
178
  logger.info(
237
- f"队列 '{self.queue_name}' 已绑定到交换机 '{self.exchange_name}',路由键: {bind_routing_key} (主机: {self._active_host})")
179
+ f"队列 '{self.queue_name}' 已绑定到交换机 '{self.exchange_name}',路由键: {bind_routing_key}")
238
180
  return True
239
- except asyncio.TimeoutError:
240
- logger.warning(
241
- f"队列 '{self.queue_name}' 绑定超时(第{attempt+1}次尝试)(主机: {self._active_host})")
242
181
  except Exception as e:
243
- logger.error(
244
- f"队列绑定失败(第{attempt+1}次尝试): {str(e)} (主机: {self._active_host})")
245
-
246
- if attempt < retries:
182
+ logger.warning(
183
+ f"队列绑定失败(第{attempt+1}次尝试): {str(e)}")
184
+ if attempt < MAX_RETRY_COUNT:
247
185
  await asyncio.sleep(1)
248
-
249
- self._queue_bound = False
250
186
  return False
251
187
 
252
- async def _try_connect_host(self, host: str) -> AbstractConnection:
253
- """尝试连接单个主机"""
254
- try:
255
- logger.debug(f"尝试连接主机: {host}:{self.port}")
256
- return await asyncio.wait_for(
257
- connect_robust(
258
- host=host,
259
- port=self.port,
260
- login=self.username,
261
- password=self.password,
262
- virtualhost=self.virtualhost,
263
- heartbeat=self.heartbeat,
264
- loop=asyncio.get_event_loop(),
265
- client_properties={
266
- "connection_name": f"{self.app_name}@{host}"
267
- }
268
- ),
269
- timeout=self.connection_timeout
270
- )
271
- except Exception as e:
272
- logger.warning(f"连接主机 {host}:{self.port} 失败: {str(e)}")
273
- raise
274
-
275
188
  async def connect(self, force_reconnect: bool = False, declare_queue: bool = True) -> None:
276
189
  """
277
- 建立与RabbitMQ集群的连接(支持多节点故障转移)并初始化所需资源
278
-
279
- :param force_reconnect: 是否强制重新连接
280
- :param declare_queue: 是否声明队列
190
+ 从连接池获取资源并初始化(交换机、队列)
281
191
  """
282
192
  logger.debug(
283
193
  f"连接参数 - force_reconnect={force_reconnect}, "
284
- f"declare_queue={declare_queue}, create_if_not_exists={self.create_if_not_exists}, "
285
- f"主机列表: {self.hosts}"
194
+ f"declare_queue={declare_queue}, create_if_not_exists={self.create_if_not_exists}"
286
195
  )
287
196
 
288
197
  # 如果已连接且不强制重连,则直接返回
@@ -293,185 +202,121 @@ class RabbitMQClient:
293
202
  if self._reconnect_task and not self._reconnect_task.done():
294
203
  self._reconnect_task.cancel()
295
204
 
296
- logger.debug(
297
- f"尝试连接RabbitMQ集群 - 主机数量: {len(self.hosts)}, "
298
- f"虚拟主机: {self.virtualhost}, 队列: {self.queue_name}"
299
- )
300
-
301
205
  # 重置状态
302
206
  self._exchange_exists = False
303
207
  self._queue_exists = False
304
208
  self._queue_bound = False
305
- self._active_host = None
306
209
 
307
210
  retries = 0
308
211
  last_exception = None
309
212
 
310
213
  while retries < self.max_reconnection_attempts:
311
- # 遍历所有主机尝试连接(故障转移)
312
- for host in self.hosts:
313
- try:
314
- # 关闭现有连接
315
- if self.connection and not self.connection.is_closed:
316
- await self.connection.close()
317
-
318
- # 尝试连接当前主机
319
- self.connection = await self._try_connect_host(host)
320
- self._active_host = host
321
-
322
- # 创建通道
323
- self.channel = await asyncio.wait_for(
324
- self.connection.channel(),
325
- timeout=self.rpc_timeout
326
- )
327
-
328
- # 启用发布确认
329
- # await self.channel.confirm_delivery()
330
-
331
- # 设置预取计数,控制消息公平分发
332
- await self.channel.set_qos(prefetch_count=self.prefetch_count)
333
-
334
- # 处理交换机
335
- exchange_exists = await self._check_exchange_exists()
336
- if not exchange_exists:
337
- if self.create_if_not_exists:
338
- # 创建交换机
339
- self.exchange = await asyncio.wait_for(
340
- self.channel.declare_exchange(
341
- name=self.exchange_name,
342
- type=self.exchange_type,
343
- durable=self.durable,
344
- auto_delete=self.auto_delete
345
- ),
346
- timeout=self.rpc_timeout
347
- )
348
- self._exchange_exists = True
349
- logger.info(
350
- f"已创建交换机 '{self.exchange_name}' (主机: {self._active_host})")
351
- else:
352
- raise Exception(
353
- f"交换机 '{self.exchange_name}' 不存在且不允许自动创建 (主机: {self._active_host})")
354
- else:
355
- # 获取已有交换机
214
+ try:
215
+ # 从池获取通道
216
+ self.channel = await self._get_channel()
217
+ await self.channel.set_qos(prefetch_count=self.prefetch_count)
218
+
219
+ # 处理交换机
220
+ exchange_exists = await self._check_exchange_exists(self.channel)
221
+ if not exchange_exists:
222
+ if self.create_if_not_exists:
223
+ # 创建交换机
356
224
  self.exchange = await asyncio.wait_for(
357
- self.channel.get_exchange(self.exchange_name),
225
+ self.channel.declare_exchange(
226
+ name=self.exchange_name,
227
+ type=self.exchange_type,
228
+ durable=self.durable,
229
+ auto_delete=self.auto_delete
230
+ ),
358
231
  timeout=self.rpc_timeout
359
232
  )
360
- logger.info(
361
- f"使用已存在的交换机 '{self.exchange_name}' (主机: {self._active_host})")
362
-
363
- # 处理队列
364
- if declare_queue and self.queue_name:
365
- queue_exists = await self._check_queue_exists()
366
-
367
- if not queue_exists:
368
- if not self.create_if_not_exists:
369
- raise Exception(
370
- f"队列 '{self.queue_name}' 不存在且不允许自动创建 (主机: {self._active_host})")
371
-
372
- # 创建队列
373
- self.queue = await asyncio.wait_for(
374
- self.channel.declare_queue(
375
- name=self.queue_name,
376
- durable=self.durable,
377
- auto_delete=self.auto_delete,
378
- exclusive=False
379
- ),
380
- timeout=self.rpc_timeout
381
- )
382
- self._queue_exists = True
383
- self.actual_queue_name = self.queue_name
384
- logger.info(
385
- f"已创建队列 '{self.queue_name}' (主机: {self._active_host})")
386
- else:
387
- # 获取已有队列
388
- self.queue = await asyncio.wait_for(
389
- self.channel.get_queue(self.queue_name),
390
- timeout=self.rpc_timeout
391
- )
392
- self.actual_queue_name = self.queue_name
393
- logger.info(
394
- f"使用已存在的队列 '{self.queue_name}' (主机: {self._active_host})")
395
-
396
- # 绑定队列到交换机
397
- if self.queue and self.exchange:
398
- bound = await self._bind_queue()
399
- if not bound:
400
- raise Exception(
401
- f"队列 '{self.queue_name}' 绑定到交换机 '{self.exchange_name}' 失败 (主机: {self._active_host})")
402
- else:
403
- raise Exception(
404
- "队列或交换机未正确初始化 (主机: {self._active_host})")
233
+ logger.info(f"已创建交换机 '{self.exchange_name}'")
405
234
  else:
406
- # 不声明队列时的状态处理
407
- self.queue = None
408
- self.actual_queue_name = None
409
- self._queue_exists = False
410
- self._queue_bound = False
411
- logger.debug(
412
- f"跳过队列 '{self.queue_name}' 的声明和绑定 (主机: {self._active_host})")
413
-
414
- # 验证连接状态
415
- if not self.is_connected:
416
235
  raise Exception(
417
- f"连接验证失败,状态异常 (主机: {self._active_host})")
418
-
419
- # 如果之前在消费,重新开始消费
420
- if self._is_consuming and self.message_handler:
421
- await self.start_consuming()
422
-
423
- # 启动连接监控和保活任务
424
- self._start_monitoring()
425
- self._start_keepalive()
236
+ f"交换机 '{self.exchange_name}' 不存在且不允许自动创建")
237
+ else:
238
+ # 获取已有交换机
239
+ self.exchange = await self.channel.get_exchange(self.exchange_name)
240
+ logger.info(f"使用已存在的交换机 '{self.exchange_name}'")
241
+
242
+ # 处理队列
243
+ if declare_queue and self.queue_name:
244
+ queue_exists = await self._check_queue_exists(self.channel)
245
+
246
+ if not queue_exists:
247
+ if not self.create_if_not_exists:
248
+ raise Exception(
249
+ f"队列 '{self.queue_name}' 不存在且不允许自动创建")
250
+
251
+ # 创建队列
252
+ self.queue = await asyncio.wait_for(
253
+ self.channel.declare_queue(
254
+ name=self.queue_name,
255
+ durable=self.durable,
256
+ auto_delete=self.auto_delete,
257
+ exclusive=False
258
+ ),
259
+ timeout=self.rpc_timeout
260
+ )
261
+ self.actual_queue_name = self.queue_name
262
+ logger.info(f"已创建队列 '{self.queue_name}'")
263
+ else:
264
+ # 获取已有队列
265
+ self.queue = await self.channel.get_queue(self.queue_name)
266
+ self.actual_queue_name = self.queue_name
267
+ logger.info(f"使用已存在的队列 '{self.queue_name}'")
268
+
269
+ # 绑定队列到交换机
270
+ if self.queue and self.exchange:
271
+ bound = await self._bind_queue(self.channel, self.queue, self.exchange)
272
+ if not bound:
273
+ raise Exception(f"队列 '{self.queue_name}' 绑定到交换机失败")
274
+ else:
275
+ # 不声明队列时的状态处理
276
+ self.queue = None
277
+ self.actual_queue_name = None
278
+ logger.debug(f"跳过队列 '{self.queue_name}' 的声明和绑定")
426
279
 
427
- self._update_activity_timestamp()
428
- logger.info(
429
- f"RabbitMQ客户端连接成功 (主机: {self._active_host}, 队列: {self.actual_queue_name})")
430
- return
280
+ # 验证连接状态
281
+ if not self.is_connected:
282
+ raise Exception("连接验证失败,状态异常")
431
283
 
432
- except Exception as e:
433
- last_exception = e
434
- logger.warning(
435
- f"主机 {host} 连接处理失败: {str(e)},尝试下一个主机...")
436
- # 清理当前失败的连接资源
437
- if self.connection and not self.connection.is_closed:
438
- await self.connection.close()
439
- self.connection = None
440
- self.channel = None
441
- self.exchange = None
442
- self.queue = None
284
+ # 如果之前在消费,重新开始消费
285
+ if self._is_consuming and self.message_handler:
286
+ await self.start_consuming()
443
287
 
444
- # 所有主机都尝试失败,进行重试
445
- retries += 1
446
- logger.warning(
447
- f"集群连接失败({retries}/{self.max_reconnection_attempts}),所有主机均无法连接,重试中...")
288
+ # 启动连接监控和保活任务
289
+ self._start_monitoring()
290
+ self._start_keepalive()
448
291
 
449
- if retries < self.max_reconnection_attempts:
450
- await asyncio.sleep(self.reconnection_delay)
292
+ self._update_activity_timestamp()
293
+ logger.info(f"RabbitMQ客户端初始化成功 (队列: {self.actual_queue_name})")
294
+ return
451
295
 
452
- logger.error(f"最终连接失败: {str(last_exception)}")
296
+ except Exception as e:
297
+ last_exception = e
298
+ logger.warning(f"资源初始化失败: {str(e)},重试中...")
299
+ # 释放当前通道(放回池并重新获取)
300
+ self.channel = None
301
+ retries += 1
302
+ if retries < self.max_reconnection_attempts:
303
+ await asyncio.sleep(self.reconnection_delay)
304
+
305
+ logger.error(f"最终初始化失败: {str(last_exception)}")
453
306
  raise Exception(
454
- f"经过{self.max_reconnection_attempts}次重试后仍无法连接到RabbitMQ集群。最后错误: {str(last_exception)}")
307
+ f"经过{self.max_reconnection_attempts}次重试后仍无法初始化客户端。最后错误: {str(last_exception)}")
455
308
 
456
309
  def _start_monitoring(self) -> None:
457
- """启动连接和消费监控任务,支持集群节点故障检测"""
310
+ """启动连接和消费监控任务"""
458
311
  if self._closed or (self._monitor_task and not self._monitor_task.done()):
459
312
  return
460
313
 
461
314
  async def monitor():
462
- while not self._closed and self.connection:
315
+ while not self._closed and self.channel:
463
316
  try:
464
- # 检查连接状态
465
- if self.connection.is_closed:
466
- logger.warning(
467
- f"检测到RabbitMQ连接已关闭 (主机: {self._active_host}),将尝试重连到集群其他节点")
468
- await self._schedule_reconnect()
469
- return
470
-
471
317
  # 检查通道状态
472
- if self.channel and self.channel.is_closed:
473
- logger.warning(
474
- f"检测到RabbitMQ通道已关闭 (主机: {self._active_host}),将尝试重建")
318
+ if self.channel.is_closed:
319
+ logger.warning("检测到通道已关闭,尝试重建")
475
320
  await self._recreate_channel()
476
321
  continue
477
322
 
@@ -479,55 +324,49 @@ class RabbitMQClient:
479
324
  if self._is_consuming:
480
325
  current_time = asyncio.get_event_loop().time()
481
326
  if current_time - self._last_message_processed > self.consumption_stall_threshold:
482
- # logger.warning(
483
- # f"检测到消费停滞超过 {self.consumption_stall_threshold} 秒 (主机: {self._active_host}),将重启消费者")
484
- if self._is_consuming and self.message_handler:
327
+ if self._processing_message_ids:
328
+ logger.warning(
329
+ f"消费停滞,但有 {len(self._processing_message_ids)} 个消息正在处理,暂不重启")
330
+ else:
331
+ # 确实无消息处理,再重启
485
332
  await self.stop_consuming()
486
333
  await asyncio.sleep(1)
487
334
  await self.start_consuming()
488
- # logger.info("消费者已重启以恢复消费")
489
335
  except Exception as e:
490
336
  logger.error(f"监控任务出错: {str(e)}")
491
337
  await asyncio.sleep(1)
492
338
 
493
- await asyncio.sleep(5) # 每5秒检查一次
339
+ await asyncio.sleep(30)
494
340
 
495
341
  self._monitor_task = asyncio.create_task(monitor())
496
342
 
497
343
  async def _recreate_channel(self) -> None:
498
- """重建通道并恢复绑定和消费,支持当前节点故障时的快速恢复"""
499
344
  try:
500
- # 连接已关闭时触发完整重连(尝试其他节点)
501
- if not self.connection or self.connection.is_closed:
502
- logger.warning("连接已关闭,触发集群重连")
503
- await self._schedule_reconnect()
504
- return
505
-
506
- # 重新创建通道
507
- self.channel = await self.connection.channel()
345
+ # 无需手动释放,上下文管理器会自动处理
346
+ self.channel = await self._get_channel()
508
347
  await self.channel.set_qos(prefetch_count=self.prefetch_count)
509
348
 
510
349
  # 重新获取交换机
511
350
  self.exchange = await self.channel.get_exchange(self.exchange_name)
512
351
 
513
- # 重新绑定队列和交换机
352
+ # 重新绑定队列
514
353
  if self.queue_name:
515
354
  self.queue = await self.channel.get_queue(self.queue_name)
516
355
  if self.queue and self.exchange:
517
- await self._bind_queue()
356
+ await self._bind_queue(self.channel, self.queue, self.exchange)
518
357
 
519
358
  # 重新开始消费
520
359
  if self._is_consuming and self.message_handler:
521
360
  await self.start_consuming()
522
361
 
523
- logger.info(f"通道已重新创建并恢复服务 (主机: {self._active_host})")
362
+ logger.info("通道已重建并恢复服务")
524
363
  self._update_activity_timestamp()
525
364
  except Exception as e:
526
- logger.error(f"通道重建失败,触发集群重连: {str(e)} (主机: {self._active_host})")
527
- await self._schedule_reconnect()
365
+ logger.error(f"通道重建失败: {str(e)},触发重连")
366
+ await self.connect(force_reconnect=True)
528
367
 
529
368
  def _start_keepalive(self) -> None:
530
- """启动连接保活任务,维护集群连接心跳"""
369
+ """启动连接保活任务"""
531
370
  if self._closed or (self._keepalive_task and not self._keepalive_task.done()):
532
371
  return
533
372
 
@@ -535,52 +374,44 @@ class RabbitMQClient:
535
374
  while not self._closed and self.is_connected:
536
375
  current_time = asyncio.get_event_loop().time()
537
376
  # 检查是否超过指定时间无活动
538
- if current_time - self._last_activity_timestamp > self.heartbeat * 1.5:
377
+ if current_time - self._last_activity_timestamp > self.connection_pool.heartbeat * 1.5:
539
378
  logger.debug(
540
- f"连接 {self.heartbeat*1.5}s 无活动,执行保活检查 (主机: {self._active_host})")
379
+ f"连接 {self.connection_pool.heartbeat*1.5}s 无活动,执行保活检查")
541
380
  try:
542
- if self.connection and self.connection.is_closed:
543
- logger.warning("连接已关闭,触发集群重连")
544
- await self._schedule_reconnect()
381
+ if self.channel.is_closed:
382
+ logger.warning("连接已关闭,触发重连")
383
+ await self.connect(force_reconnect=True)
545
384
  return
546
385
 
547
- # 执行轻量级操作保持连接活跃
548
- if self.channel:
549
- await asyncio.wait_for(
550
- self.channel.declare_exchange(
551
- name=self.exchange_name,
552
- type=self.exchange_type,
553
- passive=True # 仅检查存在性
554
- ),
555
- timeout=5
556
- )
557
-
386
+ # 轻量级操作保持连接活跃
387
+ await asyncio.wait_for(
388
+ self.channel.declare_exchange(
389
+ name=self.exchange_name,
390
+ type=self.exchange_type,
391
+ passive=True
392
+ ),
393
+ timeout=5
394
+ )
558
395
  self._update_activity_timestamp()
559
- except asyncio.TimeoutError:
560
- logger.warning(
561
- f"保活检查超时,触发集群重连 (主机: {self._active_host})")
562
- await self._schedule_reconnect()
563
396
  except Exception as e:
564
- logger.warning(
565
- f"保活检查失败: {str(e)},触发集群重连 (主机: {self._active_host})")
566
- await self._schedule_reconnect()
397
+ logger.warning(f"保活检查失败: {str(e)},触发重连")
398
+ await self.connect(force_reconnect=True)
567
399
 
568
- await asyncio.sleep(self.heartbeat / 2) # 每心跳间隔的一半检查一次
400
+ await asyncio.sleep(self.connection_pool.heartbeat / 2)
569
401
 
570
402
  self._keepalive_task = asyncio.create_task(keepalive())
571
403
 
572
404
  async def _schedule_reconnect(self) -> None:
573
- """安排重新连接(尝试集群中的所有节点)"""
405
+ """安排重新连接"""
574
406
  if self._reconnect_task and not self._reconnect_task.done():
575
407
  return
576
408
 
577
- logger.info(f"将在 {self.reconnection_delay} 秒后尝试重新连接到RabbitMQ集群...")
409
+ logger.info(f"将在 {self.reconnection_delay} 秒后尝试重新连接...")
578
410
 
579
411
  async def reconnect():
580
412
  try:
581
413
  await asyncio.sleep(self.reconnection_delay)
582
414
  if not self._closed:
583
- # 重连时尝试所有节点
584
415
  await self.connect(force_reconnect=True)
585
416
  except Exception as e:
586
417
  logger.error(f"重连任务失败: {str(e)}")
@@ -590,7 +421,7 @@ class RabbitMQClient:
590
421
  self._reconnect_task = asyncio.create_task(reconnect())
591
422
 
592
423
  async def close(self) -> None:
593
- """关闭连接并清理资源"""
424
+ """关闭客户端并释放资源"""
594
425
  self._closed = True
595
426
  self._is_consuming = False
596
427
 
@@ -604,24 +435,12 @@ class RabbitMQClient:
604
435
  except asyncio.CancelledError:
605
436
  pass
606
437
 
607
- # 关闭连接
608
- if self.connection and not self.connection.is_closed:
609
- try:
610
- await asyncio.wait_for(self.connection.close(), timeout=5)
611
- except Exception as e:
612
- logger.warning(f"关闭连接时出错 (主机: {self._active_host}): {str(e)}")
613
-
614
438
  # 重置状态
615
- self.connection = None
616
439
  self.channel = None
617
440
  self.exchange = None
618
441
  self.queue = None
619
- self._exchange_exists = False
620
- self._queue_exists = False
621
- self._queue_bound = False
622
442
  self._consumer_tag = None
623
443
  self._processing_message_ids.clear()
624
- self._active_host = None
625
444
 
626
445
  logger.info("RabbitMQ客户端已关闭")
627
446
 
@@ -633,15 +452,7 @@ class RabbitMQClient:
633
452
  headers: Optional[Dict[str, Any]] = None,
634
453
  delivery_mode: DeliveryMode = DeliveryMode.PERSISTENT
635
454
  ) -> None:
636
- """
637
- 发布消息到交换机(自动处理连接故障并重试)
638
-
639
- :param message_body: 消息体,可以是字符串或字典
640
- :param routing_key: 路由键,如未指定则使用实例的routing_key
641
- :param content_type: 内容类型
642
- :param headers: 消息头
643
- :param delivery_mode: 投递模式,持久化或非持久化
644
- """
455
+ """发布消息(从池获取通道,自动重试)"""
645
456
  if not self.is_connected:
646
457
  logger.warning("连接已关闭,尝试重连后发布消息")
647
458
  await self.connect(force_reconnect=True)
@@ -667,17 +478,23 @@ class RabbitMQClient:
667
478
 
668
479
  # 发布消息(带重试机制)
669
480
  retry_count = 0
670
- while retry_count < 2: # 最多重试2次
481
+ max_retries = 2
482
+ while retry_count < max_retries:
671
483
  try:
672
- await self.exchange.publish(
673
- message,
674
- routing_key=routing_key or self.routing_key or '#',
675
- mandatory=True,
676
- timeout=5.0
677
- )
484
+ # 从池获取新通道用于发布(避免长时间占用消费通道)
485
+ async with self.connection_pool.channel_pool.acquire() as publish_channel:
486
+ exchange = await publish_channel.get_exchange(self.exchange_name)
487
+ confirmed = await exchange.publish(
488
+ message,
489
+ routing_key=routing_key or self.routing_key or '#',
490
+ mandatory=True,
491
+ timeout=5.0
492
+ )
493
+ if not confirmed:
494
+ raise Exception("消息未被服务器确认接收")
495
+
678
496
  self._update_activity_timestamp()
679
- logger.debug(
680
- f"消息已发布到交换机 '{self.exchange_name}' (主机: {self._active_host})")
497
+ logger.debug(f"消息已发布到交换机 '{self.exchange_name}'")
681
498
  return
682
499
  except (ConnectionClosed, ChannelInvalidStateError):
683
500
  retry_count += 1
@@ -686,38 +503,22 @@ class RabbitMQClient:
686
503
  except Exception as e:
687
504
  retry_count += 1
688
505
  logger.error(f"消息发布失败 (重试次数: {retry_count}): {str(e)}")
689
- if retry_count < 2:
506
+ if retry_count < max_retries:
690
507
  await asyncio.sleep(1)
691
508
 
692
509
  raise Exception(f"消息发布失败,经过{retry_count}次重试仍未成功")
693
510
 
694
- def set_message_handler(
695
- self,
696
- handler: Callable[
697
- [Union[Dict[str, Any], str], AbstractIncomingMessage],
698
- Coroutine[Any, Any, None]
699
- ]
700
- ) -> None:
701
- """
702
- 设置消息处理函数
703
-
704
- :param handler: 消息处理函数,接收解析后的消息和原始消息对象
705
- """
511
+ # 以下方法(消息消费相关)逻辑与原有保持一致,仅适配连接池
512
+ def set_message_handler(self, handler):
706
513
  self.message_handler = handler
707
514
 
708
515
  async def start_consuming(self) -> ConsumerTag:
709
- """
710
- 开始消费消息
711
-
712
- :return: 消费者标签
713
- """
714
516
  if self._is_consuming:
715
517
  logger.debug("已经在消费中,返回现有consumer_tag")
716
518
  if self._consumer_tag:
717
519
  return self._consumer_tag
718
520
  raise Exception("消费已启动但未获取到consumer_tag")
719
521
 
720
- # 确保连接和队列已准备好
721
522
  if not self.is_connected:
722
523
  await self.connect()
723
524
 
@@ -728,27 +529,23 @@ class RabbitMQClient:
728
529
  raise Exception("未设置消息处理函数")
729
530
 
730
531
  self._is_consuming = True
731
- logger.info(
732
- f"开始消费队列: {self.actual_queue_name} (主机: {self._active_host})")
532
+ logger.info(f"开始消费队列: {self.actual_queue_name}")
733
533
 
734
534
  try:
735
- # 开始消费,使用aio-pika的队列消费方法
736
535
  self._consumer_tag = await self.queue.consume(
737
536
  self._message_wrapper,
738
537
  no_ack=False # 手动确认消息
739
538
  )
740
539
 
741
540
  logger.info(
742
- f"消费者已启动,队列: {self.actual_queue_name}, tag: {self._consumer_tag}, 主机: {self._active_host}")
541
+ f"消费者已启动,队列: {self.actual_queue_name}, tag: {self._consumer_tag}")
743
542
  return self._consumer_tag
744
543
  except Exception as e:
745
544
  self._is_consuming = False
746
- logger.error(
747
- f"启动消费失败: {str(e)} (主机: {self._active_host})", exc_info=True)
545
+ logger.error(f"启动消费失败: {str(e)}", exc_info=True)
748
546
  raise
749
547
 
750
548
  async def _safe_cancel_consumer(self) -> bool:
751
- """安全取消消费者"""
752
549
  if not self._consumer_tag or not self.queue or not self.channel:
753
550
  return True
754
551
 
@@ -757,36 +554,25 @@ class RabbitMQClient:
757
554
  self.queue.cancel(self._consumer_tag),
758
555
  timeout=self.rpc_timeout
759
556
  )
760
- logger.info(
761
- f"消费者 {self._consumer_tag} 已取消 (主机: {self._active_host})")
557
+ logger.info(f"消费者 {self._consumer_tag} 已取消")
762
558
  return True
763
- except (ChannelInvalidStateError, ConnectionClosed):
764
- logger.warning(f"取消消费者失败:通道或连接已关闭 (主机: {self._active_host})")
765
- return False
766
- except asyncio.TimeoutError:
767
- logger.warning(f"取消消费者超时 (主机: {self._active_host})")
768
- return False
769
559
  except Exception as e:
770
- logger.error(f"取消消费者异常: {str(e)} (主机: {self._active_host})")
560
+ logger.error(f"取消消费者异常: {str(e)}")
771
561
  return False
772
562
 
773
563
  async def stop_consuming(self) -> None:
774
- """停止消费消息,等待正在处理的消息完成"""
775
564
  if not self._is_consuming:
776
565
  return
777
566
 
778
567
  self._is_consuming = False
779
568
 
780
- # 取消消费者,停止接收新消息
781
569
  if self._consumer_tag and self.queue:
782
570
  await self._safe_cancel_consumer()
783
571
 
784
572
  # 等待所有正在处理的消息完成
785
573
  if self._processing_message_ids:
786
574
  logger.info(
787
- f"等待 {len(self._processing_message_ids)} 个正在处理的消息完成... (主机: {self._active_host})"
788
- )
789
- # 循环等待直到所有消息处理完成
575
+ f"等待 {len(self._processing_message_ids)} 个正在处理的消息完成...")
790
576
  while self._processing_message_ids and not self._closed:
791
577
  await asyncio.sleep(0.1)
792
578
 
@@ -794,11 +580,9 @@ class RabbitMQClient:
794
580
  self._consumer_tag = None
795
581
  self._processing_message_ids.clear()
796
582
 
797
- logger.info(
798
- f"已停止消费队列: {self.actual_queue_name} (主机: {self._active_host})")
583
+ logger.info(f"已停止消费队列: {self.actual_queue_name}")
799
584
 
800
585
  async def _parse_message(self, message: AbstractIncomingMessage) -> Union[Dict[str, Any], str]:
801
- """解析消息体"""
802
586
  try:
803
587
  body_str = message.body.decode('utf-8')
804
588
  self._update_activity_timestamp()
@@ -807,63 +591,54 @@ class RabbitMQClient:
807
591
  return json.loads(body_str)
808
592
  return body_str
809
593
  except json.JSONDecodeError:
810
- logger.warning(f"消息解析JSON失败,返回原始字符串 (主机: {self._active_host})")
594
+ logger.warning(f"消息解析JSON失败,返回原始字符串")
811
595
  return body_str
812
596
  except Exception as e:
813
- logger.error(f"消息解析出错: {str(e)} (主机: {self._active_host})")
597
+ logger.error(f"消息解析出错: {str(e)}")
814
598
  return message.body.decode('utf-8')
815
599
 
816
600
  async def _message_wrapper(self, message: AbstractIncomingMessage) -> None:
817
- """消息处理包装器,处理消息接收、解析、分发和确认"""
818
601
  if not self.message_handler or not self._is_consuming:
819
- logger.warning("未设置消息处理器或已停止消费,确认消息")
820
- await message.ack()
602
+ logger.warning("未设置消息处理器或已停止消费")
603
+ # await message.ack()
821
604
  return
822
605
 
823
- # 跟踪消息ID,防止重复处理
824
606
  message_id = message.message_id or str(id(message))
825
607
  if message_id in self._processing_message_ids:
826
- logger.warning(
827
- f"检测到重复处理的消息ID: {message_id},直接确认 (主机: {self._active_host})")
608
+ logger.warning(f"检测到重复处理的消息ID: {message_id},直接确认")
828
609
  await message.ack()
829
610
  return
830
611
 
831
612
  self._processing_message_ids.add(message_id)
832
613
 
833
614
  try:
834
- logger.debug(
835
- f"收到队列 {self.actual_queue_name} 的消息: {message_id} (主机: {self._active_host})")
615
+ logger.debug(f"收到队列 {self.actual_queue_name} 的消息: {message_id}")
836
616
 
837
- # 解析消息
838
617
  parsed_data = await self._parse_message(message)
618
+ await self.message_handler(MQMsgModel(** parsed_data), message)
839
619
 
840
- await self.message_handler(MQMsgModel(**parsed_data), message)
841
-
842
- # 处理成功,确认消息
843
620
  await message.ack()
844
621
  self._update_activity_timestamp()
845
622
  self._update_message_processed_timestamp()
846
- logger.debug(f"消息 {message_id} 处理完成并确认 (主机: {self._active_host})")
623
+ logger.debug(f"消息 {message_id} 处理完成并确认")
847
624
 
848
625
  except Exception as e:
849
- # 处理失败,根据重试次数决定是否重新发布
850
626
  current_headers = message.headers or {}
851
627
  retry_count = current_headers.get('x-retry-count', 0)
852
628
  retry_count += 1
853
629
 
854
630
  logger.error(
855
- f"消息 {message_id} 处理出错(第{retry_count}次重试): {str(e)} (主机: {self._active_host})",
631
+ f"消息 {message_id} 处理出错(第{retry_count}次重试): {str(e)}",
856
632
  exc_info=True
857
633
  )
858
634
 
859
635
  if retry_count >= MAX_RETRY_COUNT:
860
636
  logger.error(
861
- f"消息 {message_id} 已达到最大重试次数({MAX_RETRY_COUNT}次),标记为失败 (主机: {self._active_host})")
637
+ f"消息 {message_id} 已达到最大重试次数({MAX_RETRY_COUNT}次),标记为失败")
862
638
  await message.ack()
863
639
  self._update_activity_timestamp()
864
640
  return
865
641
 
866
- # 准备重新发布的消息
867
642
  new_headers = current_headers.copy()
868
643
  new_headers['x-retry-count'] = retry_count
869
644
 
@@ -874,10 +649,8 @@ class RabbitMQClient:
874
649
  delivery_mode=message.delivery_mode
875
650
  )
876
651
 
877
- # 拒绝原消息(不重新入队)
878
652
  await message.reject(requeue=False)
879
653
 
880
- # 重新发布消息
881
654
  if self.exchange:
882
655
  await self.exchange.publish(
883
656
  new_message,
@@ -886,10 +659,8 @@ class RabbitMQClient:
886
659
  timeout=5.0
887
660
  )
888
661
  self._update_activity_timestamp()
889
- logger.info(
890
- f"消息 {message_id} 已重新发布,当前重试次数: {retry_count} (主机: {self._active_host})")
662
+ logger.info(f"消息 {message_id} 已重新发布,当前重试次数: {retry_count}")
891
663
  finally:
892
- # 移除消息ID跟踪
893
664
  if message_id in self._processing_message_ids:
894
665
  self._processing_message_ids.remove(message_id)
895
666