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.
- sycommon/rabbitmq/rabbitmq_client.py +123 -145
- sycommon/rabbitmq/rabbitmq_pool.py +126 -205
- sycommon/rabbitmq/rabbitmq_service.py +77 -127
- sycommon/services.py +11 -21
- {sycommon_python_lib-0.1.49.dist-info → sycommon_python_lib-0.1.51.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.49.dist-info → sycommon_python_lib-0.1.51.dist-info}/RECORD +9 -9
- {sycommon_python_lib-0.1.49.dist-info → sycommon_python_lib-0.1.51.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.49.dist-info → sycommon_python_lib-0.1.51.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.49.dist-info → sycommon_python_lib-0.1.51.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import random
|
|
3
|
-
from typing import Optional, List,
|
|
4
|
-
from aio_pika import connect_robust,
|
|
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
|
-
"""
|
|
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 =
|
|
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.
|
|
57
|
-
|
|
58
|
-
# 消费者通道跟踪
|
|
54
|
+
self._channel: Optional[RobustChannel] = None # 单通道(原生自动恢复)
|
|
55
|
+
# 消费者通道跟踪(独立于主通道)
|
|
59
56
|
self._consumer_channels: Dict[str,
|
|
60
|
-
Tuple[
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
|
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.
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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.
|
|
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[
|
|
209
|
-
"""
|
|
210
|
-
# 快速校验状态
|
|
166
|
+
async def acquire_channel(self) -> Tuple[RobustChannel, AbstractRobustConnection]:
|
|
167
|
+
"""获取单通道(返回 (通道, 连接) 元组,保持API兼容)"""
|
|
211
168
|
async with self._lock:
|
|
212
|
-
|
|
213
|
-
raise RuntimeError("通道池未初始化,请先调用init_pools()")
|
|
169
|
+
# 原子化状态校验
|
|
214
170
|
if self._is_shutdown:
|
|
215
|
-
raise RuntimeError("
|
|
216
|
-
if not self.
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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,
|
|
271
|
-
|
|
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,
|
|
279
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
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
|
-
|
|
402
|
-
|
|
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"
|
|
323
|
+
logger.warning(f"关闭主通道失败: {str(e)}")
|
|
324
|
+
self._channel = None
|
|
405
325
|
|
|
406
326
|
# 3. 关闭连接(终止原生自动重连)
|
|
407
327
|
if self._connection:
|
|
408
328
|
try:
|
|
409
|
-
|
|
410
|
-
|
|
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单通道客户端已完全关闭")
|