sycommon-python-lib 0.1.16__py3-none-any.whl → 0.1.17__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.
- sycommon/rabbitmq/rabbitmq_client.py +194 -427
- sycommon/rabbitmq/rabbitmq_pool.py +104 -0
- sycommon/rabbitmq/rabbitmq_service.py +88 -85
- {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.17.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.17.dist-info}/RECORD +8 -7
- {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.17.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.17.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.17.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
5
|
-
from aio_pika import
|
|
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
|
|
26
|
+
RabbitMQ客户端(基于连接池),支持集群多节点配置
|
|
28
27
|
提供自动故障转移、连接恢复和消息可靠性保障
|
|
29
28
|
"""
|
|
30
29
|
|
|
31
30
|
def __init__(
|
|
32
31
|
self,
|
|
33
|
-
|
|
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
|
|
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.
|
|
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
|
|
165
|
-
"""
|
|
166
|
-
if not self.
|
|
167
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
150
|
+
if not self.queue_name:
|
|
194
151
|
return False
|
|
195
|
-
|
|
196
152
|
try:
|
|
197
|
-
#
|
|
153
|
+
# 使用被动模式检查队列
|
|
198
154
|
await asyncio.wait_for(
|
|
199
|
-
|
|
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
|
|
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(
|
|
169
|
+
for attempt in range(MAX_RETRY_COUNT + 1):
|
|
226
170
|
try:
|
|
227
171
|
await asyncio.wait_for(
|
|
228
|
-
|
|
229
|
-
|
|
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}
|
|
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.
|
|
244
|
-
f"队列绑定失败(第{attempt+1}次尝试): {str(e)}
|
|
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
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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.
|
|
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"
|
|
418
|
-
|
|
419
|
-
#
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
self.
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return
|
|
280
|
+
# 验证连接状态
|
|
281
|
+
if not self.is_connected:
|
|
282
|
+
raise Exception("连接验证失败,状态异常")
|
|
431
283
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
f"集群连接失败({retries}/{self.max_reconnection_attempts}),所有主机均无法连接,重试中...")
|
|
288
|
+
# 启动连接监控和保活任务
|
|
289
|
+
self._start_monitoring()
|
|
290
|
+
self._start_keepalive()
|
|
448
291
|
|
|
449
|
-
|
|
450
|
-
|
|
292
|
+
self._update_activity_timestamp()
|
|
293
|
+
logger.info(f"RabbitMQ客户端初始化成功 (队列: {self.actual_queue_name})")
|
|
294
|
+
return
|
|
451
295
|
|
|
452
|
-
|
|
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}
|
|
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.
|
|
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
|
|
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,13 +324,10 @@ 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
327
|
if self._is_consuming and self.message_handler:
|
|
485
328
|
await self.stop_consuming()
|
|
486
329
|
await asyncio.sleep(1)
|
|
487
330
|
await self.start_consuming()
|
|
488
|
-
# logger.info("消费者已重启以恢复消费")
|
|
489
331
|
except Exception as e:
|
|
490
332
|
logger.error(f"监控任务出错: {str(e)}")
|
|
491
333
|
await asyncio.sleep(1)
|
|
@@ -495,39 +337,32 @@ class RabbitMQClient:
|
|
|
495
337
|
self._monitor_task = asyncio.create_task(monitor())
|
|
496
338
|
|
|
497
339
|
async def _recreate_channel(self) -> None:
|
|
498
|
-
"""重建通道并恢复绑定和消费,支持当前节点故障时的快速恢复"""
|
|
499
340
|
try:
|
|
500
|
-
#
|
|
501
|
-
|
|
502
|
-
logger.warning("连接已关闭,触发集群重连")
|
|
503
|
-
await self._schedule_reconnect()
|
|
504
|
-
return
|
|
505
|
-
|
|
506
|
-
# 重新创建通道
|
|
507
|
-
self.channel = await self.connection.channel()
|
|
341
|
+
# 无需手动释放,上下文管理器会自动处理
|
|
342
|
+
self.channel = await self._get_channel()
|
|
508
343
|
await self.channel.set_qos(prefetch_count=self.prefetch_count)
|
|
509
344
|
|
|
510
345
|
# 重新获取交换机
|
|
511
346
|
self.exchange = await self.channel.get_exchange(self.exchange_name)
|
|
512
347
|
|
|
513
|
-
#
|
|
348
|
+
# 重新绑定队列
|
|
514
349
|
if self.queue_name:
|
|
515
350
|
self.queue = await self.channel.get_queue(self.queue_name)
|
|
516
351
|
if self.queue and self.exchange:
|
|
517
|
-
await self._bind_queue()
|
|
352
|
+
await self._bind_queue(self.channel, self.queue, self.exchange)
|
|
518
353
|
|
|
519
354
|
# 重新开始消费
|
|
520
355
|
if self._is_consuming and self.message_handler:
|
|
521
356
|
await self.start_consuming()
|
|
522
357
|
|
|
523
|
-
logger.info(
|
|
358
|
+
logger.info("通道已重建并恢复服务")
|
|
524
359
|
self._update_activity_timestamp()
|
|
525
360
|
except Exception as e:
|
|
526
|
-
logger.error(f"
|
|
527
|
-
await self.
|
|
361
|
+
logger.error(f"通道重建失败: {str(e)},触发重连")
|
|
362
|
+
await self.connect(force_reconnect=True)
|
|
528
363
|
|
|
529
364
|
def _start_keepalive(self) -> None:
|
|
530
|
-
"""
|
|
365
|
+
"""启动连接保活任务"""
|
|
531
366
|
if self._closed or (self._keepalive_task and not self._keepalive_task.done()):
|
|
532
367
|
return
|
|
533
368
|
|
|
@@ -535,52 +370,44 @@ class RabbitMQClient:
|
|
|
535
370
|
while not self._closed and self.is_connected:
|
|
536
371
|
current_time = asyncio.get_event_loop().time()
|
|
537
372
|
# 检查是否超过指定时间无活动
|
|
538
|
-
if current_time - self._last_activity_timestamp > self.heartbeat * 1.5:
|
|
373
|
+
if current_time - self._last_activity_timestamp > self.connection_pool.heartbeat * 1.5:
|
|
539
374
|
logger.debug(
|
|
540
|
-
f"连接 {self.heartbeat*1.5}s 无活动,执行保活检查
|
|
375
|
+
f"连接 {self.connection_pool.heartbeat*1.5}s 无活动,执行保活检查")
|
|
541
376
|
try:
|
|
542
|
-
if self.
|
|
543
|
-
logger.warning("
|
|
544
|
-
await self.
|
|
377
|
+
if self.channel.is_closed:
|
|
378
|
+
logger.warning("连接已关闭,触发重连")
|
|
379
|
+
await self.connect(force_reconnect=True)
|
|
545
380
|
return
|
|
546
381
|
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
self.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
)
|
|
557
|
-
|
|
382
|
+
# 轻量级操作保持连接活跃
|
|
383
|
+
await asyncio.wait_for(
|
|
384
|
+
self.channel.declare_exchange(
|
|
385
|
+
name=self.exchange_name,
|
|
386
|
+
type=self.exchange_type,
|
|
387
|
+
passive=True
|
|
388
|
+
),
|
|
389
|
+
timeout=5
|
|
390
|
+
)
|
|
558
391
|
self._update_activity_timestamp()
|
|
559
|
-
except asyncio.TimeoutError:
|
|
560
|
-
logger.warning(
|
|
561
|
-
f"保活检查超时,触发集群重连 (主机: {self._active_host})")
|
|
562
|
-
await self._schedule_reconnect()
|
|
563
392
|
except Exception as e:
|
|
564
|
-
logger.warning(
|
|
565
|
-
|
|
566
|
-
await self._schedule_reconnect()
|
|
393
|
+
logger.warning(f"保活检查失败: {str(e)},触发重连")
|
|
394
|
+
await self.connect(force_reconnect=True)
|
|
567
395
|
|
|
568
|
-
await asyncio.sleep(self.heartbeat / 2)
|
|
396
|
+
await asyncio.sleep(self.connection_pool.heartbeat / 2)
|
|
569
397
|
|
|
570
398
|
self._keepalive_task = asyncio.create_task(keepalive())
|
|
571
399
|
|
|
572
400
|
async def _schedule_reconnect(self) -> None:
|
|
573
|
-
"""
|
|
401
|
+
"""安排重新连接"""
|
|
574
402
|
if self._reconnect_task and not self._reconnect_task.done():
|
|
575
403
|
return
|
|
576
404
|
|
|
577
|
-
logger.info(f"将在 {self.reconnection_delay}
|
|
405
|
+
logger.info(f"将在 {self.reconnection_delay} 秒后尝试重新连接...")
|
|
578
406
|
|
|
579
407
|
async def reconnect():
|
|
580
408
|
try:
|
|
581
409
|
await asyncio.sleep(self.reconnection_delay)
|
|
582
410
|
if not self._closed:
|
|
583
|
-
# 重连时尝试所有节点
|
|
584
411
|
await self.connect(force_reconnect=True)
|
|
585
412
|
except Exception as e:
|
|
586
413
|
logger.error(f"重连任务失败: {str(e)}")
|
|
@@ -590,7 +417,7 @@ class RabbitMQClient:
|
|
|
590
417
|
self._reconnect_task = asyncio.create_task(reconnect())
|
|
591
418
|
|
|
592
419
|
async def close(self) -> None:
|
|
593
|
-
"""
|
|
420
|
+
"""关闭客户端并释放资源"""
|
|
594
421
|
self._closed = True
|
|
595
422
|
self._is_consuming = False
|
|
596
423
|
|
|
@@ -604,24 +431,12 @@ class RabbitMQClient:
|
|
|
604
431
|
except asyncio.CancelledError:
|
|
605
432
|
pass
|
|
606
433
|
|
|
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
434
|
# 重置状态
|
|
615
|
-
self.connection = None
|
|
616
435
|
self.channel = None
|
|
617
436
|
self.exchange = None
|
|
618
437
|
self.queue = None
|
|
619
|
-
self._exchange_exists = False
|
|
620
|
-
self._queue_exists = False
|
|
621
|
-
self._queue_bound = False
|
|
622
438
|
self._consumer_tag = None
|
|
623
439
|
self._processing_message_ids.clear()
|
|
624
|
-
self._active_host = None
|
|
625
440
|
|
|
626
441
|
logger.info("RabbitMQ客户端已关闭")
|
|
627
442
|
|
|
@@ -633,15 +448,7 @@ class RabbitMQClient:
|
|
|
633
448
|
headers: Optional[Dict[str, Any]] = None,
|
|
634
449
|
delivery_mode: DeliveryMode = DeliveryMode.PERSISTENT
|
|
635
450
|
) -> 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
|
-
"""
|
|
451
|
+
"""发布消息(从池获取通道,自动重试)"""
|
|
645
452
|
if not self.is_connected:
|
|
646
453
|
logger.warning("连接已关闭,尝试重连后发布消息")
|
|
647
454
|
await self.connect(force_reconnect=True)
|
|
@@ -667,17 +474,23 @@ class RabbitMQClient:
|
|
|
667
474
|
|
|
668
475
|
# 发布消息(带重试机制)
|
|
669
476
|
retry_count = 0
|
|
670
|
-
|
|
477
|
+
max_retries = 2
|
|
478
|
+
while retry_count < max_retries:
|
|
671
479
|
try:
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
480
|
+
# 从池获取新通道用于发布(避免长时间占用消费通道)
|
|
481
|
+
async with self.connection_pool.channel_pool.acquire() as publish_channel:
|
|
482
|
+
exchange = await publish_channel.get_exchange(self.exchange_name)
|
|
483
|
+
confirmed = await exchange.publish(
|
|
484
|
+
message,
|
|
485
|
+
routing_key=routing_key or self.routing_key or '#',
|
|
486
|
+
mandatory=True,
|
|
487
|
+
timeout=5.0
|
|
488
|
+
)
|
|
489
|
+
if not confirmed:
|
|
490
|
+
raise Exception("消息未被服务器确认接收")
|
|
491
|
+
|
|
678
492
|
self._update_activity_timestamp()
|
|
679
|
-
logger.debug(
|
|
680
|
-
f"消息已发布到交换机 '{self.exchange_name}' (主机: {self._active_host})")
|
|
493
|
+
logger.debug(f"消息已发布到交换机 '{self.exchange_name}'")
|
|
681
494
|
return
|
|
682
495
|
except (ConnectionClosed, ChannelInvalidStateError):
|
|
683
496
|
retry_count += 1
|
|
@@ -686,38 +499,22 @@ class RabbitMQClient:
|
|
|
686
499
|
except Exception as e:
|
|
687
500
|
retry_count += 1
|
|
688
501
|
logger.error(f"消息发布失败 (重试次数: {retry_count}): {str(e)}")
|
|
689
|
-
if retry_count <
|
|
502
|
+
if retry_count < max_retries:
|
|
690
503
|
await asyncio.sleep(1)
|
|
691
504
|
|
|
692
505
|
raise Exception(f"消息发布失败,经过{retry_count}次重试仍未成功")
|
|
693
506
|
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
"""
|
|
507
|
+
# 以下方法(消息消费相关)逻辑与原有保持一致,仅适配连接池
|
|
508
|
+
def set_message_handler(self, handler):
|
|
706
509
|
self.message_handler = handler
|
|
707
510
|
|
|
708
511
|
async def start_consuming(self) -> ConsumerTag:
|
|
709
|
-
"""
|
|
710
|
-
开始消费消息
|
|
711
|
-
|
|
712
|
-
:return: 消费者标签
|
|
713
|
-
"""
|
|
714
512
|
if self._is_consuming:
|
|
715
513
|
logger.debug("已经在消费中,返回现有consumer_tag")
|
|
716
514
|
if self._consumer_tag:
|
|
717
515
|
return self._consumer_tag
|
|
718
516
|
raise Exception("消费已启动但未获取到consumer_tag")
|
|
719
517
|
|
|
720
|
-
# 确保连接和队列已准备好
|
|
721
518
|
if not self.is_connected:
|
|
722
519
|
await self.connect()
|
|
723
520
|
|
|
@@ -728,27 +525,23 @@ class RabbitMQClient:
|
|
|
728
525
|
raise Exception("未设置消息处理函数")
|
|
729
526
|
|
|
730
527
|
self._is_consuming = True
|
|
731
|
-
logger.info(
|
|
732
|
-
f"开始消费队列: {self.actual_queue_name} (主机: {self._active_host})")
|
|
528
|
+
logger.info(f"开始消费队列: {self.actual_queue_name}")
|
|
733
529
|
|
|
734
530
|
try:
|
|
735
|
-
# 开始消费,使用aio-pika的队列消费方法
|
|
736
531
|
self._consumer_tag = await self.queue.consume(
|
|
737
532
|
self._message_wrapper,
|
|
738
533
|
no_ack=False # 手动确认消息
|
|
739
534
|
)
|
|
740
535
|
|
|
741
536
|
logger.info(
|
|
742
|
-
f"消费者已启动,队列: {self.actual_queue_name}, tag: {self._consumer_tag}
|
|
537
|
+
f"消费者已启动,队列: {self.actual_queue_name}, tag: {self._consumer_tag}")
|
|
743
538
|
return self._consumer_tag
|
|
744
539
|
except Exception as e:
|
|
745
540
|
self._is_consuming = False
|
|
746
|
-
logger.error(
|
|
747
|
-
f"启动消费失败: {str(e)} (主机: {self._active_host})", exc_info=True)
|
|
541
|
+
logger.error(f"启动消费失败: {str(e)}", exc_info=True)
|
|
748
542
|
raise
|
|
749
543
|
|
|
750
544
|
async def _safe_cancel_consumer(self) -> bool:
|
|
751
|
-
"""安全取消消费者"""
|
|
752
545
|
if not self._consumer_tag or not self.queue or not self.channel:
|
|
753
546
|
return True
|
|
754
547
|
|
|
@@ -757,36 +550,25 @@ class RabbitMQClient:
|
|
|
757
550
|
self.queue.cancel(self._consumer_tag),
|
|
758
551
|
timeout=self.rpc_timeout
|
|
759
552
|
)
|
|
760
|
-
logger.info(
|
|
761
|
-
f"消费者 {self._consumer_tag} 已取消 (主机: {self._active_host})")
|
|
553
|
+
logger.info(f"消费者 {self._consumer_tag} 已取消")
|
|
762
554
|
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
555
|
except Exception as e:
|
|
770
|
-
logger.error(f"取消消费者异常: {str(e)}
|
|
556
|
+
logger.error(f"取消消费者异常: {str(e)}")
|
|
771
557
|
return False
|
|
772
558
|
|
|
773
559
|
async def stop_consuming(self) -> None:
|
|
774
|
-
"""停止消费消息,等待正在处理的消息完成"""
|
|
775
560
|
if not self._is_consuming:
|
|
776
561
|
return
|
|
777
562
|
|
|
778
563
|
self._is_consuming = False
|
|
779
564
|
|
|
780
|
-
# 取消消费者,停止接收新消息
|
|
781
565
|
if self._consumer_tag and self.queue:
|
|
782
566
|
await self._safe_cancel_consumer()
|
|
783
567
|
|
|
784
568
|
# 等待所有正在处理的消息完成
|
|
785
569
|
if self._processing_message_ids:
|
|
786
570
|
logger.info(
|
|
787
|
-
f"等待 {len(self._processing_message_ids)} 个正在处理的消息完成...
|
|
788
|
-
)
|
|
789
|
-
# 循环等待直到所有消息处理完成
|
|
571
|
+
f"等待 {len(self._processing_message_ids)} 个正在处理的消息完成...")
|
|
790
572
|
while self._processing_message_ids and not self._closed:
|
|
791
573
|
await asyncio.sleep(0.1)
|
|
792
574
|
|
|
@@ -794,11 +576,9 @@ class RabbitMQClient:
|
|
|
794
576
|
self._consumer_tag = None
|
|
795
577
|
self._processing_message_ids.clear()
|
|
796
578
|
|
|
797
|
-
logger.info(
|
|
798
|
-
f"已停止消费队列: {self.actual_queue_name} (主机: {self._active_host})")
|
|
579
|
+
logger.info(f"已停止消费队列: {self.actual_queue_name}")
|
|
799
580
|
|
|
800
581
|
async def _parse_message(self, message: AbstractIncomingMessage) -> Union[Dict[str, Any], str]:
|
|
801
|
-
"""解析消息体"""
|
|
802
582
|
try:
|
|
803
583
|
body_str = message.body.decode('utf-8')
|
|
804
584
|
self._update_activity_timestamp()
|
|
@@ -807,63 +587,54 @@ class RabbitMQClient:
|
|
|
807
587
|
return json.loads(body_str)
|
|
808
588
|
return body_str
|
|
809
589
|
except json.JSONDecodeError:
|
|
810
|
-
logger.warning(f"消息解析JSON失败,返回原始字符串
|
|
590
|
+
logger.warning(f"消息解析JSON失败,返回原始字符串")
|
|
811
591
|
return body_str
|
|
812
592
|
except Exception as e:
|
|
813
|
-
logger.error(f"消息解析出错: {str(e)}
|
|
593
|
+
logger.error(f"消息解析出错: {str(e)}")
|
|
814
594
|
return message.body.decode('utf-8')
|
|
815
595
|
|
|
816
596
|
async def _message_wrapper(self, message: AbstractIncomingMessage) -> None:
|
|
817
|
-
"""消息处理包装器,处理消息接收、解析、分发和确认"""
|
|
818
597
|
if not self.message_handler or not self._is_consuming:
|
|
819
|
-
logger.warning("
|
|
820
|
-
await message.ack()
|
|
598
|
+
logger.warning("未设置消息处理器或已停止消费")
|
|
599
|
+
# await message.ack()
|
|
821
600
|
return
|
|
822
601
|
|
|
823
|
-
# 跟踪消息ID,防止重复处理
|
|
824
602
|
message_id = message.message_id or str(id(message))
|
|
825
603
|
if message_id in self._processing_message_ids:
|
|
826
|
-
logger.warning(
|
|
827
|
-
f"检测到重复处理的消息ID: {message_id},直接确认 (主机: {self._active_host})")
|
|
604
|
+
logger.warning(f"检测到重复处理的消息ID: {message_id},直接确认")
|
|
828
605
|
await message.ack()
|
|
829
606
|
return
|
|
830
607
|
|
|
831
608
|
self._processing_message_ids.add(message_id)
|
|
832
609
|
|
|
833
610
|
try:
|
|
834
|
-
logger.debug(
|
|
835
|
-
f"收到队列 {self.actual_queue_name} 的消息: {message_id} (主机: {self._active_host})")
|
|
611
|
+
logger.debug(f"收到队列 {self.actual_queue_name} 的消息: {message_id}")
|
|
836
612
|
|
|
837
|
-
# 解析消息
|
|
838
613
|
parsed_data = await self._parse_message(message)
|
|
614
|
+
await self.message_handler(MQMsgModel(** parsed_data), message)
|
|
839
615
|
|
|
840
|
-
await self.message_handler(MQMsgModel(**parsed_data), message)
|
|
841
|
-
|
|
842
|
-
# 处理成功,确认消息
|
|
843
616
|
await message.ack()
|
|
844
617
|
self._update_activity_timestamp()
|
|
845
618
|
self._update_message_processed_timestamp()
|
|
846
|
-
logger.debug(f"消息 {message_id} 处理完成并确认
|
|
619
|
+
logger.debug(f"消息 {message_id} 处理完成并确认")
|
|
847
620
|
|
|
848
621
|
except Exception as e:
|
|
849
|
-
# 处理失败,根据重试次数决定是否重新发布
|
|
850
622
|
current_headers = message.headers or {}
|
|
851
623
|
retry_count = current_headers.get('x-retry-count', 0)
|
|
852
624
|
retry_count += 1
|
|
853
625
|
|
|
854
626
|
logger.error(
|
|
855
|
-
f"消息 {message_id} 处理出错(第{retry_count}次重试): {str(e)}
|
|
627
|
+
f"消息 {message_id} 处理出错(第{retry_count}次重试): {str(e)}",
|
|
856
628
|
exc_info=True
|
|
857
629
|
)
|
|
858
630
|
|
|
859
631
|
if retry_count >= MAX_RETRY_COUNT:
|
|
860
632
|
logger.error(
|
|
861
|
-
f"消息 {message_id} 已达到最大重试次数({MAX_RETRY_COUNT}次),标记为失败
|
|
633
|
+
f"消息 {message_id} 已达到最大重试次数({MAX_RETRY_COUNT}次),标记为失败")
|
|
862
634
|
await message.ack()
|
|
863
635
|
self._update_activity_timestamp()
|
|
864
636
|
return
|
|
865
637
|
|
|
866
|
-
# 准备重新发布的消息
|
|
867
638
|
new_headers = current_headers.copy()
|
|
868
639
|
new_headers['x-retry-count'] = retry_count
|
|
869
640
|
|
|
@@ -874,10 +645,8 @@ class RabbitMQClient:
|
|
|
874
645
|
delivery_mode=message.delivery_mode
|
|
875
646
|
)
|
|
876
647
|
|
|
877
|
-
# 拒绝原消息(不重新入队)
|
|
878
648
|
await message.reject(requeue=False)
|
|
879
649
|
|
|
880
|
-
# 重新发布消息
|
|
881
650
|
if self.exchange:
|
|
882
651
|
await self.exchange.publish(
|
|
883
652
|
new_message,
|
|
@@ -886,10 +655,8 @@ class RabbitMQClient:
|
|
|
886
655
|
timeout=5.0
|
|
887
656
|
)
|
|
888
657
|
self._update_activity_timestamp()
|
|
889
|
-
logger.info(
|
|
890
|
-
f"消息 {message_id} 已重新发布,当前重试次数: {retry_count} (主机: {self._active_host})")
|
|
658
|
+
logger.info(f"消息 {message_id} 已重新发布,当前重试次数: {retry_count}")
|
|
891
659
|
finally:
|
|
892
|
-
# 移除消息ID跟踪
|
|
893
660
|
if message_id in self._processing_message_ids:
|
|
894
661
|
self._processing_message_ids.remove(message_id)
|
|
895
662
|
|