sycommon-python-lib 0.1.49__tar.gz → 0.1.51__tar.gz

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.
Files changed (65) hide show
  1. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/PKG-INFO +1 -1
  2. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/pyproject.toml +1 -1
  3. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/rabbitmq/rabbitmq_client.py +123 -145
  4. sycommon_python_lib-0.1.51/src/sycommon/rabbitmq/rabbitmq_pool.py +338 -0
  5. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/rabbitmq/rabbitmq_service.py +77 -127
  6. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/services.py +11 -21
  7. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
  8. sycommon_python_lib-0.1.49/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -417
  9. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/README.md +0 -0
  10. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/setup.cfg +0 -0
  11. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/command/cli.py +0 -0
  12. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/__init__.py +0 -0
  13. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/Config.py +0 -0
  14. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/DatabaseConfig.py +0 -0
  15. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/EmbeddingConfig.py +0 -0
  16. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/LLMConfig.py +0 -0
  17. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/MQConfig.py +0 -0
  18. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/RerankerConfig.py +0 -0
  19. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/config/__init__.py +0 -0
  20. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/database/base_db_service.py +0 -0
  21. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/database/database_service.py +0 -0
  22. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/health/__init__.py +0 -0
  23. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/health/health_check.py +0 -0
  24. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/health/metrics.py +0 -0
  25. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/health/ping.py +0 -0
  26. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/logging/__init__.py +0 -0
  27. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/logging/kafka_log.py +0 -0
  28. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/logging/logger_wrapper.py +0 -0
  29. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/logging/sql_logger.py +0 -0
  30. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/__init__.py +0 -0
  31. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/context.py +0 -0
  32. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/cors.py +0 -0
  33. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/docs.py +0 -0
  34. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/exception.py +0 -0
  35. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/middleware.py +0 -0
  36. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/monitor_memory.py +0 -0
  37. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/mq.py +0 -0
  38. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/timeout.py +0 -0
  39. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/middleware/traceid.py +0 -0
  40. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/__init__.py +0 -0
  41. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/base_http.py +0 -0
  42. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/log.py +0 -0
  43. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/mqlistener_config.py +0 -0
  44. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/mqmsg_model.py +0 -0
  45. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/mqsend_config.py +0 -0
  46. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/models/sso_user.py +0 -0
  47. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/sse/__init__.py +0 -0
  48. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/sse/event.py +0 -0
  49. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/sse/sse.py +0 -0
  50. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/__init__.py +0 -0
  51. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/example.py +0 -0
  52. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/example2.py +0 -0
  53. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/feign.py +0 -0
  54. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/feign_client.py +0 -0
  55. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/nacos_service.py +0 -0
  56. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/synacos/param.py +0 -0
  57. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/tools/__init__.py +0 -0
  58. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/tools/docs.py +0 -0
  59. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/tools/snowflake.py +0 -0
  60. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon/tools/timing.py +0 -0
  61. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
  62. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  63. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  64. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
  65. {sycommon_python_lib-0.1.49 → sycommon_python_lib-0.1.51}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.49
3
+ Version: 0.1.51
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.1.49"
3
+ version = "0.1.51"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -15,7 +15,6 @@ from sycommon.rabbitmq.rabbitmq_pool import RabbitMQConnectionPool
15
15
  from sycommon.logging.kafka_log import SYLogger
16
16
  from sycommon.models.mqmsg_model import MQMsgModel
17
17
 
18
-
19
18
  logger = SYLogger
20
19
 
21
20
 
@@ -23,8 +22,8 @@ class RabbitMQClient:
23
22
  """
24
23
  RabbitMQ 客户端(支持消息发布、消费、自动重连、异常重试)
25
24
  核心特性:
26
- 1. 基于连接池复用资源,性能优化
27
- 2. 连接/通道失效时自动重建,高可用(限制并发重连)
25
+ 1. 基于单通道连接池复用资源,性能优化
26
+ 2. 依赖连接池原生自动重连,客户端仅重建自身资源
28
27
  3. 消息发布支持重试+mandatory机制+超时控制,确保路由有效
29
28
  4. 消费支持手动ACK/NACK
30
29
  5. 兼容JSON/字符串/字典消息格式
@@ -41,7 +40,6 @@ class RabbitMQClient:
41
40
  auto_delete: bool = False,
42
41
  auto_parse_json: bool = True,
43
42
  create_if_not_exists: bool = True,
44
- prefetch_count: int = 2,
45
43
  **kwargs,
46
44
  ):
47
45
  # 依赖注入:连接池(必须已初始化)
@@ -54,8 +52,8 @@ class RabbitMQClient:
54
52
  try:
55
53
  self.exchange_type = ExchangeType(exchange_type.lower())
56
54
  except ValueError:
57
- SYLogger.warning(f"无效的exchange_type: {exchange_type},默认使用'topic'")
58
- self.exchange_type = ExchangeType.topic
55
+ logger.warning(f"无效的exchange_type: {exchange_type},默认使用'topic'")
56
+ self.exchange_type = ExchangeType.TOPIC
59
57
 
60
58
  # 队列配置
61
59
  self.queue_name = queue_name.strip() if queue_name else None
@@ -65,9 +63,6 @@ class RabbitMQClient:
65
63
  self.auto_parse_json = auto_parse_json # 自动解析JSON消息体
66
64
  self.create_if_not_exists = create_if_not_exists # 不存在则创建交换机/队列
67
65
 
68
- # 消费配置
69
- self.prefetch_count = max(1, prefetch_count) # 每次预取消息数(避免消息堆积)
70
-
71
66
  # 内部状态(资源+连接)
72
67
  self._channel: Optional[Channel] = None
73
68
  self._channel_conn: Optional[AbstractRobustConnection] = None # 通道所属连接
@@ -83,12 +78,10 @@ class RabbitMQClient:
83
78
  self._connect_lock = asyncio.Lock()
84
79
  # 跟踪连接关闭回调(用于后续移除)
85
80
  self._conn_close_callback: Optional[Callable] = None
86
- # 控制重连频率的信号量(限制并发重连数,默认1个)
81
+ # 控制重连频率的信号量(限制并发重连数)
87
82
  self._reconnect_semaphore = asyncio.Semaphore(1)
88
83
  # 固定重连间隔15秒(全局统一)
89
84
  self._RECONNECT_INTERVAL = 15
90
- # 重连任务锁(确保同一时间只有一个重连任务)
91
- self._reconnect_task_lock = asyncio.Lock()
92
85
  # 跟踪当前重连任务(避免重复创建)
93
86
  self._current_reconnect_task: Optional[asyncio.Task] = None
94
87
  # 连接失败计数器(用于告警)
@@ -102,7 +95,7 @@ class RabbitMQClient:
102
95
  if self._closed:
103
96
  return False
104
97
  try:
105
- # 校验通道+连接+核心资源都有效
98
+ # 单通道场景:校验通道+连接+核心资源都有效
106
99
  return (
107
100
  self._channel and not self._channel.is_closed
108
101
  and self._channel_conn and not self._channel_conn.is_closed
@@ -110,23 +103,52 @@ class RabbitMQClient:
110
103
  and (not self.queue_name or self._queue is not None)
111
104
  )
112
105
  except Exception as e:
113
- SYLogger.warning(f"检查连接状态失败: {str(e)}")
106
+ logger.warning(f"检查连接状态失败: {str(e)}")
114
107
  return False
115
108
 
109
+ async def _rebuild_resources(self) -> None:
110
+ """重建交换机/队列等资源(依赖已有的通道)"""
111
+ if not self._channel or self._channel.is_closed:
112
+ raise RuntimeError("无有效通道,无法重建资源")
113
+
114
+ # 1. 声明交换机
115
+ self._exchange = await self._channel.declare_exchange(
116
+ name=self.exchange_name,
117
+ type=self.exchange_type,
118
+ durable=self.durable,
119
+ auto_delete=self.auto_delete,
120
+ passive=not self.create_if_not_exists,
121
+ )
122
+ logger.info(
123
+ f"交换机重建成功: {self.exchange_name}(类型: {self.exchange_type.value})")
124
+
125
+ # 2. 声明队列(如果配置了队列名)
126
+ if self.queue_name:
127
+ self._queue = await self._channel.declare_queue(
128
+ name=self.queue_name,
129
+ durable=self.durable,
130
+ auto_delete=self.auto_delete,
131
+ passive=not self.create_if_not_exists,
132
+ )
133
+ # 绑定队列到交换机
134
+ await self._queue.bind(
135
+ exchange=self._exchange,
136
+ routing_key=self.routing_key,
137
+ )
138
+ logger.info(
139
+ f"队列重建成功: {self.queue_name} "
140
+ f"(绑定交换机: {self.exchange_name}, routing_key: {self.routing_key})"
141
+ )
142
+
116
143
  async def connect(self) -> None:
117
144
  if self._closed:
118
145
  raise RuntimeError("客户端已关闭,无法重新连接")
119
146
 
120
147
  async with self._connect_lock:
121
- # 释放旧资源(保留原有回调清理逻辑)
122
- if self._channel and self._channel_conn:
123
- try:
124
- if self._conn_close_callback and self._channel_conn:
125
- self._channel_conn.close_callbacks.discard(
126
- self._conn_close_callback)
127
- await self.connection_pool.release_channel(self._channel, self._channel_conn)
128
- except Exception as e:
129
- SYLogger.warning(f"释放旧通道失败: {str(e)}")
148
+ # 释放旧资源(回调+通道,单通道无需归还,仅清理状态)
149
+ if self._conn_close_callback and self._channel_conn:
150
+ self._channel_conn.close_callbacks.discard(
151
+ self._conn_close_callback)
130
152
  self._channel = None
131
153
  self._channel_conn = None
132
154
  self._exchange = None
@@ -134,17 +156,17 @@ class RabbitMQClient:
134
156
  self._conn_close_callback = None
135
157
 
136
158
  try:
137
- # 从连接池获取通道+连接(连接池返回的是 RobustChannel)
159
+ # 从单通道池获取通道+连接(连接池自动确保通道有效)
138
160
  self._channel, self._channel_conn = await self.connection_pool.acquire_channel()
139
161
 
140
162
  def on_conn_closed(conn: AbstractRobustConnection, exc: Optional[BaseException]):
141
163
  """连接关闭回调:触发固定间隔重连"""
142
- SYLogger.warning(
164
+ logger.warning(
143
165
  f"客户端连接关闭: {conn!r},原因: {exc}", exc_info=exc)
144
166
  self._reconnect_fail_count += 1
145
167
  # 超过阈值告警
146
168
  if self._reconnect_fail_count >= self._reconnect_alert_threshold:
147
- SYLogger.error(
169
+ logger.error(
148
170
  f"连接失败次数已达阈值({self._reconnect_alert_threshold}),请检查MQ服务状态")
149
171
  if not self._closed:
150
172
  asyncio.create_task(self._safe_reconnect())
@@ -154,97 +176,54 @@ class RabbitMQClient:
154
176
  self._channel_conn.close_callbacks.add(
155
177
  self._conn_close_callback)
156
178
 
157
- # 2. 设置预取计数(限流)
158
- await self._channel.set_qos(prefetch_count=self.prefetch_count)
159
- SYLogger.debug(f"设置预取计数: {self.prefetch_count}")
160
-
161
- # 3. 低版本 RobustChannel 说明:默认启用异步发布确认,无显式确认方法
162
- SYLogger.debug(
163
- "基于 RobustChannel 异步发布确认(低版本 aio-pika 不支持显式确认方法)")
164
-
165
- # 4. 声明交换机
166
- self._exchange = await self._channel.declare_exchange(
167
- name=self.exchange_name,
168
- type=self.exchange_type,
169
- durable=self.durable,
170
- auto_delete=self.auto_delete,
171
- passive=not self.create_if_not_exists, # passive=True时,不存在则报错
172
- )
173
- SYLogger.info(
174
- f"交换机初始化成功: {self.exchange_name}(类型: {self.exchange_type.value})")
175
-
176
- # 5. 声明队列(如果配置了队列名)
177
- if self.queue_name:
178
- self._queue = await self._channel.declare_queue(
179
- name=self.queue_name,
180
- durable=self.durable,
181
- auto_delete=self.auto_delete,
182
- passive=not self.create_if_not_exists,
183
- )
184
- # 绑定队列到交换机
185
- await self._queue.bind(
186
- exchange=self._exchange,
187
- routing_key=self.routing_key,
188
- )
189
- SYLogger.info(
190
- f"队列初始化成功: {self.queue_name} "
191
- f"(绑定交换机: {self.exchange_name}, routing_key: {self.routing_key})"
192
- )
179
+ # 重建交换机/队列资源
180
+ await self._rebuild_resources()
193
181
 
194
182
  # 重连成功,重置失败计数器
195
183
  self._reconnect_fail_count = 0
196
- SYLogger.info("客户端连接初始化完成")
184
+ logger.info("客户端连接初始化完成")
197
185
  except Exception as e:
198
- SYLogger.error(f"客户端连接失败: {str(e)}", exc_info=True)
186
+ logger.error(f"客户端连接失败: {str(e)}", exc_info=True)
199
187
  # 清理异常状态
200
188
  if self._conn_close_callback and self._channel_conn:
201
189
  self._channel_conn.close_callbacks.discard(
202
190
  self._conn_close_callback)
203
- if self._channel and self._channel_conn:
204
- try:
205
- await self.connection_pool.release_channel(self._channel, self._channel_conn)
206
- except:
207
- pass
208
191
  self._channel = None
209
192
  self._channel_conn = None
210
- # 触发重连(固定间隔)
193
+ # 触发重连
211
194
  if not self._closed:
212
195
  asyncio.create_task(self._safe_reconnect())
213
196
  raise
214
197
 
215
198
  async def _safe_reconnect(self):
216
- """安全重连:信号量控制并发+固定15秒间隔,避免短时间大量重连"""
217
- # 1. 信号量控制:限制同时进行的重连任务数(默认1个)
199
+ """安全重连:信号量控制并发+固定15秒间隔"""
218
200
  async with self._reconnect_semaphore:
219
- # 2. 检查是否已有重连任务在运行(双重保障)
201
+ # 检查是否已有重连任务在运行
220
202
  if self._current_reconnect_task and not self._current_reconnect_task.done():
221
- SYLogger.debug("已有重连任务在运行,跳过重复触发")
203
+ logger.debug("已有重连任务在运行,跳过重复触发")
222
204
  return
223
205
 
224
- async with self._reconnect_task_lock:
225
- if self._closed or await self.is_connected:
226
- SYLogger.debug("客户端已关闭或已连接,取消重连")
227
- return
206
+ if self._closed or await self.is_connected:
207
+ logger.debug("客户端已关闭或已连接,取消重连")
208
+ return
228
209
 
229
- # 3. 固定15秒重连间隔(避免频繁重试)
230
- SYLogger.info(f"将在15秒后尝试重连...")
231
- await asyncio.sleep(self._RECONNECT_INTERVAL)
210
+ # 固定15秒重连间隔
211
+ logger.info(f"将在{self._RECONNECT_INTERVAL}秒后尝试重连...")
212
+ await asyncio.sleep(self._RECONNECT_INTERVAL)
232
213
 
233
- if self._closed or await self.is_connected:
234
- SYLogger.debug("重连等待期间客户端状态变化,取消重连")
235
- return
214
+ if self._closed or await self.is_connected:
215
+ logger.debug("重连等待期间客户端状态变化,取消重连")
216
+ return
236
217
 
237
- try:
238
- # 4. 执行重连
239
- SYLogger.info("开始重连RabbitMQ客户端...")
240
- self._current_reconnect_task = asyncio.create_task(
241
- self.connect())
242
- await self._current_reconnect_task
243
- except Exception as e:
244
- SYLogger.warning(f"重连失败: {str(e)}")
245
- # 重连失败后,不主动触发下一次(等待连接关闭回调再次触发,避免死循环)
246
- finally:
247
- self._current_reconnect_task = None
218
+ try:
219
+ logger.info("开始重连RabbitMQ客户端...")
220
+ self._current_reconnect_task = asyncio.create_task(
221
+ self.connect())
222
+ await self._current_reconnect_task
223
+ except Exception as e:
224
+ logger.warning(f"重连失败: {str(e)}")
225
+ finally:
226
+ self._current_reconnect_task = None
248
227
 
249
228
  async def set_message_handler(
250
229
  self,
@@ -256,7 +235,7 @@ class RabbitMQClient:
256
235
 
257
236
  async with self._consume_lock:
258
237
  self._message_handler = handler
259
- SYLogger.info("消息处理器设置成功")
238
+ logger.info("消息处理器设置成功")
260
239
 
261
240
  async def start_consuming(self) -> Optional[ConsumerTag]:
262
241
  """启动消息消费(支持自动重连)"""
@@ -270,7 +249,7 @@ class RabbitMQClient:
270
249
  if not await self.is_connected:
271
250
  await self.connect()
272
251
  if not self._queue:
273
- raise RuntimeError("未配置队列名,无法启动消费")
252
+ raise RuntimeError("未配置队列名或队列未创建,无法启动消费")
274
253
 
275
254
  # 2. 定义消费回调(包含异常处理和重连逻辑)
276
255
  async def consume_callback(message: AbstractIncomingMessage):
@@ -282,7 +261,7 @@ class RabbitMQClient:
282
261
  message.body.decode("utf-8"))
283
262
  msg_obj = MQMsgModel(**body_dict)
284
263
  except json.JSONDecodeError as e:
285
- SYLogger.error(
264
+ logger.error(
286
265
  f"JSON消息解析失败: {str(e)},消息体: {message.body[:100]}...")
287
266
  await message.nack(requeue=False) # 解析失败,不重入队
288
267
  return
@@ -293,51 +272,59 @@ class RabbitMQClient:
293
272
  delivery_tag=message.delivery_tag,
294
273
  )
295
274
 
296
- # 调用消息处理器(必须await,避免协程未等待警告)
275
+ # 调用消息处理器
297
276
  await self._message_handler(msg_obj, message)
298
277
 
299
- # 手动ACK(消息处理成功)
278
+ # 手动ACK
300
279
  await message.ack()
301
- SYLogger.debug(
280
+ logger.debug(
302
281
  f"消息处理成功,delivery_tag: {message.delivery_tag}")
303
282
 
304
283
  except Exception as e:
305
- SYLogger.error(
284
+ logger.error(
306
285
  f"消息处理失败,delivery_tag: {message.delivery_tag}",
307
286
  exc_info=True
308
287
  )
309
288
  # 处理失败逻辑:首次失败重入队,再次失败丢弃
310
289
  if message.redelivered:
311
- SYLogger.warning(
290
+ logger.warning(
312
291
  f"消息已重入队过,本次拒绝入队: {message.delivery_tag}")
313
292
  await message.reject(requeue=False)
314
293
  else:
315
- SYLogger.warning(f"消息重入队: {message.delivery_tag}")
294
+ logger.warning(f"消息重入队: {message.delivery_tag}")
316
295
  await message.nack(requeue=True)
317
296
 
318
- # 检查连接状态,失效则触发重连
297
+ # 连接失效则触发重连
319
298
  if not await self.is_connected:
320
- SYLogger.warning("连接已失效,触发客户端重连")
299
+ logger.warning("连接已失效,触发客户端重连")
321
300
  asyncio.create_task(self._safe_reconnect())
322
301
 
323
- # 3. 启动消费
302
+ # 3. 启动消费(单通道消费,避免阻塞发布需确保业务回调非阻塞)
324
303
  self._consumer_tag = await self._queue.consume(consume_callback)
325
- SYLogger.info(
304
+ logger.info(
326
305
  f"开始消费队列: {self._queue.name},consumer_tag: {self._consumer_tag}"
327
306
  )
328
307
  return self._consumer_tag
329
308
 
330
309
  async def stop_consuming(self) -> None:
331
- """停止消息消费"""
310
+ """停止消息消费(修复:移除 _queue.is_closed 判断)"""
332
311
  async with self._consume_lock:
333
- if self._consumer_tag and self._queue and not self._queue.is_closed:
334
- try:
335
- await self._queue.cancel(self._consumer_tag)
336
- SYLogger.info(f"停止消费成功,consumer_tag: {self._consumer_tag}")
337
- except Exception as e:
338
- SYLogger.error(f"停止消费失败: {str(e)}", exc_info=True)
339
- finally:
340
- self._consumer_tag = None
312
+ try:
313
+ # 判断 consumer_tag 和 queue 是否存在,通道状态通过 _channel 校验
314
+ if self._consumer_tag and self._queue and self._channel and not self._channel.is_closed:
315
+ # 用通道取消消费(队列无cancel方法)
316
+ await self._channel.basic_cancel(self._consumer_tag)
317
+ logger.info(
318
+ f"停止消费成功,consumer_tag: {self._consumer_tag},队列: {self._queue.name}")
319
+ elif self._consumer_tag and self._queue:
320
+ # 通道已关闭,无需执行cancel(消费已自动停止)
321
+ logger.warning(
322
+ f"通道已关闭,无需取消消费(consumer_tag: {self._consumer_tag})")
323
+ except Exception as e:
324
+ logger.error(
325
+ f"停止消费者 '{self._queue.name if self._queue else '未知队列'}' 时出错: {str(e)}", exc_info=True)
326
+ finally:
327
+ self._consumer_tag = None # 无论成败,清理消费标签
341
328
 
342
329
  async def publish(
343
330
  self,
@@ -371,7 +358,7 @@ class RabbitMQClient:
371
358
  else:
372
359
  raise TypeError(f"不支持的消息体类型: {type(message_body)}")
373
360
  except Exception as e:
374
- SYLogger.error(f"消息体序列化失败: {str(e)}", exc_info=True)
361
+ logger.error(f"消息体序列化失败: {str(e)}", exc_info=True)
375
362
  raise
376
363
 
377
364
  # 构建消息对象
@@ -387,49 +374,47 @@ class RabbitMQClient:
387
374
  try:
388
375
  # 确保连接有效
389
376
  if not await self.is_connected:
390
- SYLogger.warning(f"发布消息前连接失效,触发重连(retry: {retry})")
377
+ logger.warning(f"发布消息前连接失效,触发重连(retry: {retry})")
391
378
  await self.connect()
392
379
 
393
- # 核心:发布消息(添加 mandatory=True timeout=5.0)
380
+ # 核心:发布消息(mandatory=True 确保路由有效,timeout=5s 避免阻塞)
394
381
  publish_result = await self._exchange.publish(
395
382
  message=message,
396
383
  routing_key=self.routing_key or self.queue_name or "#",
397
- mandatory=True, # 强制路由到至少一个队列,否则返回None
398
- timeout=5.0 # 5秒超时控制,避免无限阻塞
384
+ mandatory=True,
385
+ timeout=5.0
399
386
  )
400
387
 
401
- # 处理 mandatory=True 结果:未路由到队列返回 None,直接抛出异常
388
+ # 处理 mandatory 未路由场景
402
389
  if publish_result is None:
403
390
  raise RuntimeError(
404
391
  f"消息未找到匹配的队列(routing_key: {self.routing_key}),mandatory=True 触发失败"
405
392
  )
406
393
 
407
- # 低版本 RobustChannel 异步确认,无需显式等待,仅日志说明
408
- SYLogger.info(
394
+ logger.info(
409
395
  f"消息发布成功(retry: {retry}),routing_key: {self.routing_key},"
410
396
  f"delivery_mode: {delivery_mode.value},mandatory: True,timeout: 5.0s"
411
397
  )
412
398
  return
413
399
  except asyncio.TimeoutError:
414
- SYLogger.error(
400
+ logger.error(
415
401
  f"消息发布超时(retry: {retry}/{retry_count-1}),超时时间: 5.0s"
416
402
  )
417
403
  except RuntimeError as e:
418
- # 捕获 mandatory 未路由等业务异常
419
- SYLogger.error(
404
+ logger.error(
420
405
  f"消息发布业务失败(retry: {retry}/{retry_count-1}): {str(e)}"
421
406
  )
422
407
  except Exception as e:
423
- SYLogger.error(
408
+ logger.error(
424
409
  f"消息发布失败(retry: {retry}/{retry_count-1}): {str(e)}",
425
410
  exc_info=True
426
411
  )
427
412
  # 清理失效状态,下次重试重连
428
413
  self._exchange = None
429
- # 指数退避重试间隔(0.5s, 1s, 2s...)
414
+ # 指数退避重试间隔
430
415
  await asyncio.sleep(0.5 * (2 ** retry))
431
416
 
432
- # 所有重试失败,抛出最终异常
417
+ # 所有重试失败
433
418
  raise RuntimeError(
434
419
  f"消息发布失败(已重试{retry_count}次),routing_key: {self.routing_key},"
435
420
  f"mandatory: True,timeout: 5.0s"
@@ -438,7 +423,7 @@ class RabbitMQClient:
438
423
  async def close(self) -> None:
439
424
  """关闭客户端(移除回调+释放资源)"""
440
425
  self._closed = True
441
- SYLogger.info("开始关闭RabbitMQ客户端...")
426
+ logger.info("开始关闭RabbitMQ客户端...")
442
427
 
443
428
  # 停止重连任务
444
429
  if self._current_reconnect_task and not self._current_reconnect_task.done():
@@ -446,27 +431,20 @@ class RabbitMQClient:
446
431
  try:
447
432
  await self._current_reconnect_task
448
433
  except asyncio.CancelledError:
449
- SYLogger.debug("重连任务已取消")
434
+ logger.debug("重连任务已取消")
450
435
 
451
436
  # 1. 停止消费
452
437
  await self.stop_consuming()
453
438
 
454
- # 2. 释放通道到连接池
439
+ # 2. 清理回调+状态(单通道无需归还,连接池统一管理)
455
440
  async with self._connect_lock:
456
- if self._channel and self._channel_conn:
457
- try:
458
- # 移除连接关闭回调
459
- if self._conn_close_callback:
460
- self._channel_conn.close_callbacks.discard(
461
- self._conn_close_callback)
462
- await self.connection_pool.release_channel(self._channel, self._channel_conn)
463
- SYLogger.info("通道释放成功")
464
- except Exception as e:
465
- SYLogger.error(f"通道释放失败: {str(e)}", exc_info=True)
441
+ if self._conn_close_callback and self._channel_conn:
442
+ self._channel_conn.close_callbacks.discard(
443
+ self._conn_close_callback)
466
444
  self._channel = None
467
445
  self._channel_conn = None
468
446
  self._exchange = None
469
447
  self._queue = None
470
448
  self._message_handler = None
471
449
 
472
- SYLogger.info("RabbitMQ客户端已完全关闭")
450
+ logger.info("RabbitMQ客户端已完全关闭")