sycommon-python-lib 0.1.56b5__py3-none-any.whl → 0.1.57b4__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/config/Config.py +24 -3
- sycommon/config/LangfuseConfig.py +15 -0
- sycommon/config/SentryConfig.py +13 -0
- sycommon/llm/embedding.py +269 -50
- sycommon/llm/get_llm.py +9 -218
- sycommon/llm/struct_token.py +192 -0
- sycommon/llm/sy_langfuse.py +103 -0
- sycommon/llm/usage_token.py +117 -0
- sycommon/logging/kafka_log.py +187 -433
- sycommon/middleware/exception.py +10 -16
- sycommon/middleware/timeout.py +2 -1
- sycommon/middleware/traceid.py +81 -76
- sycommon/notice/uvicorn_monitor.py +32 -27
- sycommon/rabbitmq/rabbitmq_client.py +247 -242
- sycommon/rabbitmq/rabbitmq_pool.py +201 -123
- sycommon/rabbitmq/rabbitmq_service.py +25 -843
- sycommon/rabbitmq/rabbitmq_service_client_manager.py +211 -0
- sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +73 -0
- sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +285 -0
- sycommon/rabbitmq/rabbitmq_service_core.py +117 -0
- sycommon/rabbitmq/rabbitmq_service_producer_manager.py +238 -0
- sycommon/sentry/__init__.py +0 -0
- sycommon/sentry/sy_sentry.py +35 -0
- sycommon/services.py +122 -96
- sycommon/synacos/nacos_client_base.py +121 -0
- sycommon/synacos/nacos_config_manager.py +107 -0
- sycommon/synacos/nacos_heartbeat_manager.py +144 -0
- sycommon/synacos/nacos_service.py +63 -783
- sycommon/synacos/nacos_service_discovery.py +157 -0
- sycommon/synacos/nacos_service_registration.py +270 -0
- sycommon/tools/env.py +62 -0
- sycommon/tools/merge_headers.py +20 -0
- sycommon/tools/snowflake.py +101 -153
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b4.dist-info}/METADATA +10 -8
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b4.dist-info}/RECORD +38 -20
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b4.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b4.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b5.dist-info → sycommon_python_lib-0.1.57b4.dist-info}/top_level.txt +0 -0
|
@@ -1,823 +1,38 @@
|
|
|
1
|
+
from typing import Type
|
|
1
2
|
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
from typing import (
|
|
4
|
-
Callable, Coroutine, Dict, List, Optional, Type, Union, Any
|
|
5
|
-
)
|
|
6
|
-
from pydantic import BaseModel
|
|
7
|
-
from aio_pika.abc import AbstractIncomingMessage, ConsumerTag
|
|
8
|
-
|
|
9
|
-
from sycommon.models.mqmsg_model import MQMsgModel
|
|
10
|
-
from sycommon.models.mqlistener_config import RabbitMQListenerConfig
|
|
11
|
-
from sycommon.models.mqsend_config import RabbitMQSendConfig
|
|
12
|
-
from sycommon.models.sso_user import SsoUser
|
|
13
3
|
from sycommon.logging.kafka_log import SYLogger
|
|
14
|
-
from sycommon.rabbitmq.
|
|
4
|
+
from sycommon.rabbitmq.rabbitmq_service_connection_monitor import RabbitMQConnectionMonitor
|
|
5
|
+
from sycommon.rabbitmq.rabbitmq_service_consumer_manager import RabbitMQConsumerManager
|
|
6
|
+
from sycommon.rabbitmq.rabbitmq_service_producer_manager import RabbitMQProducerManager
|
|
15
7
|
|
|
16
8
|
logger = SYLogger
|
|
17
9
|
|
|
18
10
|
|
|
19
|
-
class RabbitMQService:
|
|
11
|
+
class RabbitMQService(RabbitMQConnectionMonitor, RabbitMQProducerManager, RabbitMQConsumerManager):
|
|
20
12
|
"""
|
|
21
|
-
RabbitMQ
|
|
22
|
-
核心特性:基于单通道连接池复用资源,简化状态管理,依赖原生自动重连
|
|
13
|
+
RabbitMQ服务对外统一接口 - 保持原有API兼容
|
|
23
14
|
"""
|
|
24
|
-
|
|
25
|
-
# 保存多个客户端实例(共享单通道,仅维护配置差异)
|
|
26
|
-
_clients: Dict[str, RabbitMQClient] = {}
|
|
27
|
-
# 保存消息处理器
|
|
28
|
-
_message_handlers: Dict[str, Callable[[
|
|
29
|
-
MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]]] = {}
|
|
30
|
-
# 保存消费者任务
|
|
31
|
-
_consumer_tasks: Dict[str, asyncio.Task] = {}
|
|
32
|
-
# 保存配置信息
|
|
33
|
-
_config: Optional[dict] = None
|
|
34
|
-
# 存储发送客户端的名称
|
|
35
|
-
_sender_client_names: List[str] = []
|
|
36
|
-
# 用于控制消费者任务退出的事件
|
|
37
|
-
_consumer_events: Dict[str, asyncio.Event] = {}
|
|
38
|
-
# 存储消费者标签
|
|
39
|
-
_consumer_tags: Dict[str, ConsumerTag] = {}
|
|
40
|
-
# 异步锁,确保初始化安全
|
|
41
|
-
_init_locks: Dict[str, asyncio.Lock] = {}
|
|
42
|
-
# 标记是否有监听器和发送器
|
|
43
|
-
_has_listeners: bool = False
|
|
44
|
-
_has_senders: bool = False
|
|
45
|
-
# 消费启动超时设置
|
|
46
|
-
CONSUMER_START_TIMEOUT = 30 # 30秒超时
|
|
47
|
-
# 连接池实例(单通道)
|
|
48
|
-
_connection_pool: Optional[RabbitMQConnectionPool] = None
|
|
49
|
-
# 服务关闭标记
|
|
50
|
-
_is_shutdown: bool = False
|
|
51
|
-
# 服务关闭锁
|
|
52
|
-
_shutdown_lock = asyncio.Lock()
|
|
53
|
-
# 连接状态监控任务
|
|
54
|
-
_connection_monitor_task: Optional[asyncio.Task] = None
|
|
55
|
-
# 重连配置
|
|
56
|
-
RECONNECT_INTERVAL = 15 # 重连基础间隔(秒)
|
|
57
|
-
|
|
58
15
|
@classmethod
|
|
59
16
|
def init(cls, config: dict, has_listeners: bool = False, has_senders: bool = False) -> Type['RabbitMQService']:
|
|
60
|
-
"""初始化RabbitMQ
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# 防止重复初始化
|
|
64
|
-
if cls._config:
|
|
65
|
-
logger.warning("RabbitMQService已初始化,无需重复调用")
|
|
66
|
-
return cls
|
|
67
|
-
|
|
68
|
-
# 获取MQ配置
|
|
69
|
-
cls._config = NacosService(config).share_configs.get(
|
|
70
|
-
"mq.yml", {}).get('spring', {}).get('rabbitmq', {})
|
|
71
|
-
cls._config["APP_NAME"] = config.get("Name", "")
|
|
17
|
+
"""初始化RabbitMQ服务(保持原有接口)"""
|
|
18
|
+
# 初始化配置
|
|
19
|
+
cls.init_config(config)
|
|
72
20
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
f"端口: {cls._config.get('port')}, "
|
|
77
|
-
f"虚拟主机: {cls._config.get('virtual-host')}, "
|
|
78
|
-
f"应用名: {cls._config.get('APP_NAME')}, "
|
|
79
|
-
f"心跳: {cls._config.get('heartbeat', 30)}s"
|
|
80
|
-
)
|
|
21
|
+
# 设置模式标记
|
|
22
|
+
cls.set_mode_flags(has_listeners=has_listeners,
|
|
23
|
+
has_senders=has_senders)
|
|
81
24
|
|
|
82
|
-
#
|
|
83
|
-
cls.
|
|
84
|
-
cls._has_senders = has_senders
|
|
85
|
-
cls._is_shutdown = False
|
|
25
|
+
# 初始化连接池
|
|
26
|
+
asyncio.create_task(cls.init_connection_pool())
|
|
86
27
|
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# 启动连接监控任务(监听连接状态,自动重连)
|
|
91
|
-
cls._connection_monitor_task = asyncio.create_task(
|
|
92
|
-
cls._monitor_connections())
|
|
28
|
+
# 启动连接监控
|
|
29
|
+
cls.start_connection_monitor()
|
|
93
30
|
|
|
94
31
|
return cls
|
|
95
32
|
|
|
96
|
-
@classmethod
|
|
97
|
-
async def _init_connection_pool(cls):
|
|
98
|
-
"""初始化单通道连接池(异步操作,带重试)"""
|
|
99
|
-
if cls._connection_pool or not cls._config or cls._is_shutdown:
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
try:
|
|
103
|
-
# 解析集群节点
|
|
104
|
-
hosts_str = cls._config.get('host', "")
|
|
105
|
-
hosts_list = [host.strip()
|
|
106
|
-
for host in hosts_str.split(',') if host.strip()]
|
|
107
|
-
if not hosts_list:
|
|
108
|
-
raise ValueError("RabbitMQ集群配置为空,请检查host参数")
|
|
109
|
-
|
|
110
|
-
global_prefetch_count = cls._config.get('prefetch_count', 2)
|
|
111
|
-
|
|
112
|
-
# 创建单通道连接池(已移除channel_pool_size参数)
|
|
113
|
-
cls._connection_pool = RabbitMQConnectionPool(
|
|
114
|
-
hosts=hosts_list,
|
|
115
|
-
port=cls._config.get('port', 5672),
|
|
116
|
-
username=cls._config.get('username', ""),
|
|
117
|
-
password=cls._config.get('password', ""),
|
|
118
|
-
virtualhost=cls._config.get('virtual-host', "/"),
|
|
119
|
-
app_name=cls._config.get("APP_NAME", ""),
|
|
120
|
-
prefetch_count=global_prefetch_count,
|
|
121
|
-
heartbeat=cls._config.get('heartbeat', 30),
|
|
122
|
-
connection_timeout=cls._config.get('connection_timeout', 30),
|
|
123
|
-
reconnect_interval=cls._config.get('reconnect_interval', 5),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
# 初始化连接池
|
|
127
|
-
await asyncio.wait_for(cls._connection_pool.init_pools(), timeout=30)
|
|
128
|
-
logger.info("RabbitMQ单通道连接池初始化成功")
|
|
129
|
-
|
|
130
|
-
except Exception as e:
|
|
131
|
-
logger.error(f"RabbitMQ连接池初始化失败: {str(e)}", exc_info=True)
|
|
132
|
-
# 连接池初始化失败时重试(未关闭状态下)
|
|
133
|
-
if not cls._is_shutdown:
|
|
134
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
135
|
-
asyncio.create_task(cls._init_connection_pool())
|
|
136
|
-
|
|
137
|
-
@classmethod
|
|
138
|
-
async def _monitor_connections(cls):
|
|
139
|
-
"""连接监控任务:定期检查所有客户端连接状态,依赖连接池原生重连"""
|
|
140
|
-
logger.info("RabbitMQ连接监控任务启动")
|
|
141
|
-
while not cls._is_shutdown:
|
|
142
|
-
try:
|
|
143
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL) # 固定15秒间隔
|
|
144
|
-
|
|
145
|
-
# 跳过未初始化的连接池
|
|
146
|
-
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
147
|
-
continue
|
|
148
|
-
|
|
149
|
-
# 检查连接池本身状态
|
|
150
|
-
pool_alive = await cls._connection_pool.is_alive
|
|
151
|
-
if not pool_alive:
|
|
152
|
-
logger.error("RabbitMQ连接池已断开,等待原生自动重连")
|
|
153
|
-
continue
|
|
154
|
-
|
|
155
|
-
# 检查所有客户端连接(单通道场景下,客户端状态依赖通道有效性)
|
|
156
|
-
for client_name, client in list(cls._clients.items()):
|
|
157
|
-
try:
|
|
158
|
-
client_connected = await client.is_connected
|
|
159
|
-
if not client_connected:
|
|
160
|
-
logger.warning(
|
|
161
|
-
f"客户端 '{client_name}' 连接异常,触发重连")
|
|
162
|
-
asyncio.create_task(
|
|
163
|
-
cls._reconnect_client(client_name, client))
|
|
164
|
-
except Exception as e:
|
|
165
|
-
logger.error(
|
|
166
|
-
f"监控客户端 '{client_name}' 连接状态失败: {str(e)}", exc_info=True)
|
|
167
|
-
|
|
168
|
-
except Exception as e:
|
|
169
|
-
logger.error("RabbitMQ连接监控任务异常", exc_info=True)
|
|
170
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL) # 异常后延迟重启监控
|
|
171
|
-
|
|
172
|
-
logger.info("RabbitMQ连接监控任务停止")
|
|
173
|
-
|
|
174
|
-
@classmethod
|
|
175
|
-
async def _clean_client_resources(cls, client: RabbitMQClient):
|
|
176
|
-
"""清理客户端无效资源(单通道场景下仅重置状态,无需归还通道)"""
|
|
177
|
-
try:
|
|
178
|
-
# 先停止消费(避免消费中操作资源)
|
|
179
|
-
if client._consumer_tag:
|
|
180
|
-
await client.stop_consuming()
|
|
181
|
-
logger.debug("客户端无效资源清理完成(单通道无需归还)")
|
|
182
|
-
except Exception as e:
|
|
183
|
-
logger.warning(f"释放客户端无效资源失败: {str(e)}")
|
|
184
|
-
finally:
|
|
185
|
-
# 强制重置客户端状态(通道由连接池自动恢复)
|
|
186
|
-
client._channel = None
|
|
187
|
-
client._channel_conn = None
|
|
188
|
-
client._exchange = None
|
|
189
|
-
client._queue = None
|
|
190
|
-
client._consumer_tag = None
|
|
191
|
-
|
|
192
|
-
@classmethod
|
|
193
|
-
async def _reconnect_client(cls, client_name: str, client: RabbitMQClient) -> bool:
|
|
194
|
-
"""客户端重连(依赖连接池原生重连,仅重建客户端资源)"""
|
|
195
|
-
if cls._is_shutdown or not await cls._connection_pool.is_alive:
|
|
196
|
-
return False
|
|
197
|
-
|
|
198
|
-
# 重连冷却
|
|
199
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
200
|
-
|
|
201
|
-
try:
|
|
202
|
-
# 清理旧资源
|
|
203
|
-
await cls._clean_client_resources(client)
|
|
204
|
-
|
|
205
|
-
# 执行重连(客户端内部会从连接池获取新通道)
|
|
206
|
-
await client.connect()
|
|
207
|
-
|
|
208
|
-
# 验证重连结果
|
|
209
|
-
if await client.is_connected:
|
|
210
|
-
logger.info(f"客户端 '{client_name}' 重连成功")
|
|
211
|
-
# 如果是消费者,重新启动消费
|
|
212
|
-
if client_name in cls._message_handlers:
|
|
213
|
-
await cls.start_consumer(client_name)
|
|
214
|
-
return True
|
|
215
|
-
else:
|
|
216
|
-
logger.warning(f"客户端 '{client_name}' 重连失败:资源未完全初始化")
|
|
217
|
-
return False
|
|
218
|
-
except Exception as e:
|
|
219
|
-
logger.error(f"客户端 '{client_name}' 重连失败: {str(e)}", exc_info=True)
|
|
220
|
-
# 重连失败后,由监控任务再次触发(避免死循环)
|
|
221
|
-
return False
|
|
222
|
-
|
|
223
|
-
@classmethod
|
|
224
|
-
async def _create_client(cls, queue_name: str, **kwargs) -> RabbitMQClient:
|
|
225
|
-
"""创建客户端实例(适配单通道连接池的RabbitMQClient)"""
|
|
226
|
-
if cls._is_shutdown:
|
|
227
|
-
raise RuntimeError("RabbitMQService已关闭,无法创建客户端")
|
|
228
|
-
|
|
229
|
-
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
230
|
-
# 等待连接池初始化
|
|
231
|
-
start_time = asyncio.get_event_loop().time()
|
|
232
|
-
while not (cls._connection_pool and cls._connection_pool._initialized) and not cls._is_shutdown:
|
|
233
|
-
if asyncio.get_event_loop().time() - start_time > 30:
|
|
234
|
-
raise TimeoutError("等待连接池初始化超时")
|
|
235
|
-
await asyncio.sleep(1)
|
|
236
|
-
if cls._is_shutdown:
|
|
237
|
-
raise RuntimeError("服务关闭中,取消创建客户端")
|
|
238
|
-
|
|
239
|
-
app_name = kwargs.get('app_name', cls._config.get(
|
|
240
|
-
"APP_NAME", "")) if cls._config else ""
|
|
241
|
-
is_sender = not cls._has_listeners
|
|
242
|
-
|
|
243
|
-
# 根据组件类型决定是否允许创建队列
|
|
244
|
-
create_if_not_exists = cls._has_listeners # 只有监听器允许创建队列
|
|
245
|
-
|
|
246
|
-
# 为监听器队列名称拼接应用名
|
|
247
|
-
processed_queue_name = queue_name
|
|
248
|
-
if create_if_not_exists and not is_sender and processed_queue_name and app_name:
|
|
249
|
-
if not processed_queue_name.endswith(f".{app_name}"):
|
|
250
|
-
processed_queue_name = f"{processed_queue_name}.{app_name}"
|
|
251
|
-
logger.info(f"监听器队列名称自动拼接app-name: {processed_queue_name}")
|
|
252
|
-
else:
|
|
253
|
-
logger.info(f"监听器队列已包含app-name: {processed_queue_name}")
|
|
254
|
-
|
|
255
|
-
logger.info(
|
|
256
|
-
f"创建客户端 - 队列: {processed_queue_name}, 发送器: {is_sender}, "
|
|
257
|
-
f"允许创建: {create_if_not_exists}"
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
# 创建客户端实例(适配精简版RabbitMQClient参数)
|
|
261
|
-
client = RabbitMQClient(
|
|
262
|
-
connection_pool=cls._connection_pool,
|
|
263
|
-
exchange_name=cls._config.get(
|
|
264
|
-
'exchange_name', "system.topic.exchange"),
|
|
265
|
-
exchange_type=kwargs.get('exchange_type', "topic"),
|
|
266
|
-
queue_name=processed_queue_name,
|
|
267
|
-
routing_key=kwargs.get(
|
|
268
|
-
'routing_key', f"{processed_queue_name.split('.')[0]}.#" if processed_queue_name else "#"),
|
|
269
|
-
durable=kwargs.get('durable', True),
|
|
270
|
-
auto_delete=kwargs.get('auto_delete', False),
|
|
271
|
-
auto_parse_json=kwargs.get('auto_parse_json', True),
|
|
272
|
-
create_if_not_exists=create_if_not_exists,
|
|
273
|
-
prefetch_count=kwargs.get('prefetch_count', 2),
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
# 连接客户端(单通道场景下快速连接)
|
|
277
|
-
await client.connect()
|
|
278
|
-
|
|
279
|
-
return client
|
|
280
|
-
|
|
281
|
-
@classmethod
|
|
282
|
-
async def get_client(
|
|
283
|
-
cls,
|
|
284
|
-
client_name: str = "default", **kwargs
|
|
285
|
-
) -> RabbitMQClient:
|
|
286
|
-
"""获取或创建RabbitMQ客户端(单通道池,线程安全)"""
|
|
287
|
-
if cls._is_shutdown:
|
|
288
|
-
raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
|
|
289
|
-
|
|
290
|
-
if not cls._config:
|
|
291
|
-
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
292
|
-
|
|
293
|
-
# 等待连接池就绪
|
|
294
|
-
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
295
|
-
start_time = asyncio.get_event_loop().time()
|
|
296
|
-
while not (cls._connection_pool and cls._connection_pool._initialized) and not cls._is_shutdown:
|
|
297
|
-
if asyncio.get_event_loop().time() - start_time > 30:
|
|
298
|
-
raise TimeoutError("等待连接池初始化超时")
|
|
299
|
-
await asyncio.sleep(1)
|
|
300
|
-
if cls._is_shutdown:
|
|
301
|
-
raise RuntimeError("服务关闭中,取消获取客户端")
|
|
302
|
-
|
|
303
|
-
# 确保锁存在
|
|
304
|
-
if client_name not in cls._init_locks:
|
|
305
|
-
cls._init_locks[client_name] = asyncio.Lock()
|
|
306
|
-
|
|
307
|
-
async with cls._init_locks[client_name]:
|
|
308
|
-
# 如果客户端已存在且连接有效,直接返回
|
|
309
|
-
if client_name in cls._clients:
|
|
310
|
-
client = cls._clients[client_name]
|
|
311
|
-
is_sender = not cls._has_listeners or (
|
|
312
|
-
not kwargs.get('create_if_not_exists', True))
|
|
313
|
-
|
|
314
|
-
if await client.is_connected:
|
|
315
|
-
# 如果是监听器但队列未初始化,重新连接
|
|
316
|
-
if not is_sender and not client._queue:
|
|
317
|
-
logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
|
|
318
|
-
client.create_if_not_exists = True
|
|
319
|
-
await client.connect()
|
|
320
|
-
return client
|
|
321
|
-
else:
|
|
322
|
-
logger.info(f"客户端 '{client_name}' 连接已断开,重新连接")
|
|
323
|
-
if not is_sender:
|
|
324
|
-
client.create_if_not_exists = True
|
|
325
|
-
await client.connect()
|
|
326
|
-
return client
|
|
327
|
-
|
|
328
|
-
# 创建新客户端
|
|
329
|
-
initial_queue_name = kwargs.pop('queue_name', '')
|
|
330
|
-
is_sender = not cls._has_listeners or (
|
|
331
|
-
not kwargs.get('create_if_not_exists', True))
|
|
332
|
-
|
|
333
|
-
# 发送器特殊处理
|
|
334
|
-
if is_sender:
|
|
335
|
-
kwargs['create_if_not_exists'] = False
|
|
336
|
-
client = await cls._create_client(
|
|
337
|
-
initial_queue_name,
|
|
338
|
-
app_name=cls._config.get("APP_NAME", ""),
|
|
339
|
-
**kwargs
|
|
340
|
-
)
|
|
341
|
-
cls._clients[client_name] = client
|
|
342
|
-
return client
|
|
343
|
-
|
|
344
|
-
# 监听器逻辑(单通道支持多队列声明,无需跟踪已初始化队列)
|
|
345
|
-
kwargs['create_if_not_exists'] = True
|
|
346
|
-
|
|
347
|
-
# 创建并连接客户端
|
|
348
|
-
client = await cls._create_client(
|
|
349
|
-
initial_queue_name,
|
|
350
|
-
app_name=cls._config.get("APP_NAME", ""),
|
|
351
|
-
**kwargs
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
# 验证队列是否创建成功
|
|
355
|
-
if not client._queue:
|
|
356
|
-
logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
|
|
357
|
-
client.create_if_not_exists = True
|
|
358
|
-
await client.connect()
|
|
359
|
-
if not client._queue:
|
|
360
|
-
raise Exception(f"无法创建队列 '{initial_queue_name}'")
|
|
361
|
-
|
|
362
|
-
cls._clients[client_name] = client
|
|
363
|
-
return client
|
|
364
|
-
|
|
365
|
-
@classmethod
|
|
366
|
-
async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False, **kwargs) -> None:
|
|
367
|
-
"""设置消息发送器(适配单通道客户端)"""
|
|
368
|
-
if cls._is_shutdown:
|
|
369
|
-
logger.warning("服务已关闭,无法设置发送器")
|
|
370
|
-
return
|
|
371
|
-
|
|
372
|
-
cls._has_senders = True
|
|
373
|
-
cls._has_listeners = has_listeners
|
|
374
|
-
logger.info(f"开始设置 {len(senders)} 个消息发送器")
|
|
375
|
-
|
|
376
|
-
for idx, sender_config in enumerate(senders):
|
|
377
|
-
try:
|
|
378
|
-
if not sender_config.queue_name:
|
|
379
|
-
raise ValueError(f"发送器配置第{idx+1}项缺少queue_name")
|
|
380
|
-
|
|
381
|
-
prefetch_count = sender_config.prefetch_count
|
|
382
|
-
queue_name = sender_config.queue_name
|
|
383
|
-
app_name = cls._config.get(
|
|
384
|
-
"APP_NAME", "") if cls._config else ""
|
|
385
|
-
|
|
386
|
-
# 处理发送器队列名称,移除可能的app-name后缀
|
|
387
|
-
normalized_name = queue_name
|
|
388
|
-
if app_name and normalized_name.endswith(f".{app_name}"):
|
|
389
|
-
normalized_name = normalized_name[:-len(f".{app_name}")]
|
|
390
|
-
logger.info(f"发送器队列名称移除app-name后缀: {normalized_name}")
|
|
391
|
-
|
|
392
|
-
# 检查是否已初始化
|
|
393
|
-
if normalized_name in cls._sender_client_names:
|
|
394
|
-
logger.info(f"发送客户端 '{normalized_name}' 已存在,跳过")
|
|
395
|
-
continue
|
|
396
|
-
|
|
397
|
-
# 获取或创建客户端
|
|
398
|
-
if normalized_name in cls._clients:
|
|
399
|
-
client = cls._clients[normalized_name]
|
|
400
|
-
if not await client.is_connected:
|
|
401
|
-
await client.connect()
|
|
402
|
-
else:
|
|
403
|
-
client = await cls.get_client(
|
|
404
|
-
client_name=normalized_name,
|
|
405
|
-
exchange_type=sender_config.exchange_type,
|
|
406
|
-
durable=sender_config.durable,
|
|
407
|
-
auto_delete=sender_config.auto_delete,
|
|
408
|
-
auto_parse_json=sender_config.auto_parse_json,
|
|
409
|
-
queue_name=queue_name,
|
|
410
|
-
create_if_not_exists=False,
|
|
411
|
-
prefetch_count=prefetch_count,
|
|
412
|
-
**kwargs
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
# 记录客户端
|
|
416
|
-
if normalized_name not in cls._clients:
|
|
417
|
-
cls._clients[normalized_name] = client
|
|
418
|
-
logger.info(f"发送客户端 '{normalized_name}' 已添加")
|
|
419
|
-
|
|
420
|
-
if normalized_name not in cls._sender_client_names:
|
|
421
|
-
cls._sender_client_names.append(normalized_name)
|
|
422
|
-
logger.info(f"发送客户端 '{normalized_name}' 初始化成功")
|
|
423
|
-
|
|
424
|
-
except Exception as e:
|
|
425
|
-
logger.error(
|
|
426
|
-
f"初始化发送客户端第{idx+1}项失败: {str(e)}", exc_info=True)
|
|
427
|
-
|
|
428
|
-
logger.info(f"消息发送器设置完成,共 {len(cls._sender_client_names)} 个发送器")
|
|
429
|
-
|
|
430
|
-
@classmethod
|
|
431
|
-
async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False, **kwargs) -> None:
|
|
432
|
-
"""设置消息监听器(适配单通道客户端)"""
|
|
433
|
-
if cls._is_shutdown:
|
|
434
|
-
logger.warning("服务已关闭,无法设置监听器")
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
cls._has_listeners = True
|
|
438
|
-
cls._has_senders = has_senders
|
|
439
|
-
logger.info(f"开始设置 {len(listeners)} 个消息监听器")
|
|
440
|
-
|
|
441
|
-
for idx, listener_config in enumerate(listeners):
|
|
442
|
-
try:
|
|
443
|
-
# 转换配置并强制设置create_if_not_exists为True
|
|
444
|
-
listener_dict = listener_config.model_dump()
|
|
445
|
-
listener_dict['create_if_not_exists'] = True
|
|
446
|
-
listener_dict['prefetch_count'] = listener_config.prefetch_count
|
|
447
|
-
queue_name = listener_dict['queue_name']
|
|
448
|
-
|
|
449
|
-
logger.info(
|
|
450
|
-
f"设置监听器 {idx+1}/{len(listeners)}: {queue_name} (prefetch_count: {listener_config.prefetch_count})")
|
|
451
|
-
|
|
452
|
-
# 添加监听器
|
|
453
|
-
await cls.add_listener(**listener_dict)
|
|
454
|
-
except Exception as e:
|
|
455
|
-
logger.error(
|
|
456
|
-
f"设置监听器 {idx+1} 失败: {str(e)}", exc_info=True)
|
|
457
|
-
logger.warning("继续处理其他监听器")
|
|
458
|
-
|
|
459
|
-
# 启动所有消费者
|
|
460
|
-
await cls.start_all_consumers()
|
|
461
|
-
|
|
462
|
-
# 验证消费者启动结果
|
|
463
|
-
await cls._verify_consumers_started()
|
|
464
|
-
|
|
465
|
-
logger.info(f"消息监听器设置完成")
|
|
466
|
-
|
|
467
|
-
@classmethod
|
|
468
|
-
async def _verify_consumers_started(cls, timeout: int = 30) -> None:
|
|
469
|
-
"""验证消费者是否成功启动"""
|
|
470
|
-
start_time = asyncio.get_event_loop().time()
|
|
471
|
-
required_clients = list(cls._message_handlers.keys())
|
|
472
|
-
running_clients = []
|
|
473
|
-
|
|
474
|
-
while len(running_clients) < len(required_clients) and \
|
|
475
|
-
(asyncio.get_event_loop().time() - start_time) < timeout and \
|
|
476
|
-
not cls._is_shutdown:
|
|
477
|
-
|
|
478
|
-
running_clients = [
|
|
479
|
-
name for name, task in cls._consumer_tasks.items()
|
|
480
|
-
if not task.done() and name in cls._consumer_tags
|
|
481
|
-
]
|
|
482
|
-
|
|
483
|
-
logger.info(
|
|
484
|
-
f"消费者启动验证: {len(running_clients)}/{len(required_clients)} 已启动")
|
|
485
|
-
await asyncio.sleep(1)
|
|
486
|
-
|
|
487
|
-
failed_clients = [
|
|
488
|
-
name for name in required_clients if name not in running_clients and not cls._is_shutdown]
|
|
489
|
-
if failed_clients:
|
|
490
|
-
logger.error(f"以下消费者启动失败: {', '.join(failed_clients)}")
|
|
491
|
-
for client_name in failed_clients:
|
|
492
|
-
logger.info(f"尝试重新启动消费者: {client_name}")
|
|
493
|
-
asyncio.create_task(cls.start_consumer(client_name))
|
|
494
|
-
|
|
495
|
-
@classmethod
|
|
496
|
-
async def add_listener(
|
|
497
|
-
cls,
|
|
498
|
-
queue_name: str,
|
|
499
|
-
handler: Callable[[MQMsgModel, AbstractIncomingMessage], Coroutine[Any, Any, None]], **kwargs
|
|
500
|
-
) -> None:
|
|
501
|
-
"""添加消息监听器(线程安全,单通道场景)"""
|
|
502
|
-
if cls._is_shutdown:
|
|
503
|
-
logger.warning("服务已关闭,无法添加监听器")
|
|
504
|
-
return
|
|
505
|
-
|
|
506
|
-
if not cls._config:
|
|
507
|
-
raise ValueError("RabbitMQService尚未初始化,请先调用init方法")
|
|
508
|
-
|
|
509
|
-
if queue_name in cls._message_handlers:
|
|
510
|
-
logger.info(f"监听器 '{queue_name}' 已存在,跳过重复添加")
|
|
511
|
-
return
|
|
512
|
-
|
|
513
|
-
# 创建并初始化客户端
|
|
514
|
-
await cls.get_client(
|
|
515
|
-
client_name=queue_name,
|
|
516
|
-
queue_name=queue_name,
|
|
517
|
-
**kwargs
|
|
518
|
-
)
|
|
519
|
-
|
|
520
|
-
# 注册消息处理器
|
|
521
|
-
cls._message_handlers[queue_name] = handler
|
|
522
|
-
logger.info(f"监听器 '{queue_name}' 已添加")
|
|
523
|
-
|
|
524
|
-
@classmethod
|
|
525
|
-
async def start_all_consumers(cls) -> None:
|
|
526
|
-
"""启动所有已注册的消费者(单通道场景,避免阻塞)"""
|
|
527
|
-
if cls._is_shutdown:
|
|
528
|
-
logger.warning("服务已关闭,无法启动消费者")
|
|
529
|
-
return
|
|
530
|
-
|
|
531
|
-
for client_name in cls._message_handlers:
|
|
532
|
-
await cls.start_consumer(client_name)
|
|
533
|
-
|
|
534
|
-
@classmethod
|
|
535
|
-
async def start_consumer(cls, client_name: str) -> None:
|
|
536
|
-
"""启动指定客户端的消费者(单通道消费,需确保回调非阻塞)"""
|
|
537
|
-
if cls._is_shutdown:
|
|
538
|
-
logger.warning("服务已关闭,无法启动消费者")
|
|
539
|
-
return
|
|
540
|
-
|
|
541
|
-
# 检查任务状态,避免重复创建
|
|
542
|
-
if client_name in cls._consumer_tasks:
|
|
543
|
-
existing_task = cls._consumer_tasks[client_name]
|
|
544
|
-
if not existing_task.done():
|
|
545
|
-
# 检查任务是否处于异常状态,仅在异常时重启
|
|
546
|
-
if existing_task.exception() is not None:
|
|
547
|
-
logger.info(f"消费者 '{client_name}' 任务异常,重启")
|
|
548
|
-
existing_task.cancel()
|
|
549
|
-
else:
|
|
550
|
-
logger.info(f"消费者 '{client_name}' 已在运行中,无需重复启动")
|
|
551
|
-
return
|
|
552
|
-
else:
|
|
553
|
-
logger.info(f"消费者 '{client_name}' 任务已完成,重新启动")
|
|
554
|
-
|
|
555
|
-
if client_name not in cls._clients:
|
|
556
|
-
raise ValueError(f"RabbitMQ客户端 '{client_name}' 未初始化")
|
|
557
|
-
|
|
558
|
-
client = cls._clients[client_name]
|
|
559
|
-
handler = cls._message_handlers.get(client_name)
|
|
560
|
-
|
|
561
|
-
if not handler:
|
|
562
|
-
logger.warning(f"未找到客户端 '{client_name}' 的处理器,使用默认处理器")
|
|
563
|
-
handler = cls.default_message_handler
|
|
564
|
-
|
|
565
|
-
# 设置消息处理器
|
|
566
|
-
await client.set_message_handler(handler)
|
|
567
|
-
|
|
568
|
-
# 确保客户端已连接
|
|
569
|
-
start_time = asyncio.get_event_loop().time()
|
|
570
|
-
while not await client.is_connected and not cls._is_shutdown:
|
|
571
|
-
if asyncio.get_event_loop().time() - start_time > cls.CONSUMER_START_TIMEOUT:
|
|
572
|
-
raise TimeoutError(f"等待客户端 '{client_name}' 连接超时")
|
|
573
|
-
|
|
574
|
-
logger.info(f"等待客户端 '{client_name}' 连接就绪...")
|
|
575
|
-
await asyncio.sleep(1)
|
|
576
|
-
if cls._is_shutdown:
|
|
577
|
-
return
|
|
578
|
-
|
|
579
|
-
# 创建停止事件
|
|
580
|
-
stop_event = asyncio.Event()
|
|
581
|
-
cls._consumer_events[client_name] = stop_event
|
|
582
|
-
|
|
583
|
-
# 定义消费任务(单通道场景下,消费回调需非阻塞)
|
|
584
|
-
async def consume_task():
|
|
585
|
-
try:
|
|
586
|
-
# 启动消费,带重试机制
|
|
587
|
-
max_attempts = 3
|
|
588
|
-
attempt = 0
|
|
589
|
-
consumer_tag = None
|
|
590
|
-
|
|
591
|
-
while attempt < max_attempts and not stop_event.is_set() and not cls._is_shutdown:
|
|
592
|
-
try:
|
|
593
|
-
# 启动消费前再次校验连接和队列状态
|
|
594
|
-
if not await client.is_connected:
|
|
595
|
-
logger.info(f"消费者 '{client_name}' 连接断开,尝试重连")
|
|
596
|
-
await client.connect()
|
|
597
|
-
|
|
598
|
-
# 确保队列和处理器已就绪
|
|
599
|
-
if not client._queue:
|
|
600
|
-
raise Exception("队列未初始化完成")
|
|
601
|
-
if not client._message_handler:
|
|
602
|
-
raise Exception("消息处理器未设置")
|
|
603
|
-
|
|
604
|
-
consumer_tag = await client.start_consuming()
|
|
605
|
-
if consumer_tag:
|
|
606
|
-
break
|
|
607
|
-
except Exception as e:
|
|
608
|
-
attempt += 1
|
|
609
|
-
logger.warning(
|
|
610
|
-
f"启动消费者尝试 {attempt}/{max_attempts} 失败: {str(e)}")
|
|
611
|
-
if attempt < max_attempts:
|
|
612
|
-
await asyncio.sleep(2)
|
|
613
|
-
|
|
614
|
-
if cls._is_shutdown:
|
|
615
|
-
return
|
|
616
|
-
|
|
617
|
-
if not consumer_tag:
|
|
618
|
-
raise Exception(f"经过 {max_attempts} 次尝试仍无法启动消费者")
|
|
619
|
-
|
|
620
|
-
# 记录消费者标签
|
|
621
|
-
cls._consumer_tags[client_name] = consumer_tag
|
|
622
|
-
logger.info(
|
|
623
|
-
f"消费者 '{client_name}' 开始消费(单通道),tag: {consumer_tag},"
|
|
624
|
-
f"注意:消费回调需非阻塞,避免影响其他客户端"
|
|
625
|
-
)
|
|
626
|
-
|
|
627
|
-
# 等待停止事件
|
|
628
|
-
await stop_event.wait()
|
|
629
|
-
logger.info(f"收到停止信号,消费者 '{client_name}' 准备退出")
|
|
630
|
-
|
|
631
|
-
except asyncio.CancelledError:
|
|
632
|
-
logger.info(f"消费者 '{client_name}' 被取消")
|
|
633
|
-
except Exception as e:
|
|
634
|
-
logger.error(
|
|
635
|
-
f"消费者 '{client_name}' 错误: {str(e)}", exc_info=True)
|
|
636
|
-
# 非主动停止时尝试重启
|
|
637
|
-
if not stop_event.is_set() and not cls._is_shutdown:
|
|
638
|
-
logger.info(f"尝试重启消费者 '{client_name}'")
|
|
639
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
640
|
-
asyncio.create_task(cls.start_consumer(client_name))
|
|
641
|
-
finally:
|
|
642
|
-
# 清理资源
|
|
643
|
-
try:
|
|
644
|
-
await client.stop_consuming()
|
|
645
|
-
except Exception as e:
|
|
646
|
-
logger.error(f"停止消费者 '{client_name}' 时出错: {str(e)}")
|
|
647
|
-
|
|
648
|
-
# 移除状态记录
|
|
649
|
-
if client_name in cls._consumer_tags:
|
|
650
|
-
del cls._consumer_tags[client_name]
|
|
651
|
-
if client_name in cls._consumer_events:
|
|
652
|
-
del cls._consumer_events[client_name]
|
|
653
|
-
|
|
654
|
-
logger.info(f"消费者 '{client_name}' 已停止")
|
|
655
|
-
|
|
656
|
-
# 创建并跟踪消费任务
|
|
657
|
-
task = asyncio.create_task(
|
|
658
|
-
consume_task(), name=f"consumer-{client_name}")
|
|
659
|
-
cls._consumer_tasks[client_name] = task
|
|
660
|
-
|
|
661
|
-
# 添加任务完成回调
|
|
662
|
-
def task_done_callback(t: asyncio.Task) -> None:
|
|
663
|
-
try:
|
|
664
|
-
if t.done():
|
|
665
|
-
t.result()
|
|
666
|
-
except Exception as e:
|
|
667
|
-
logger.error(f"消费者任务 '{client_name}' 异常结束: {str(e)}")
|
|
668
|
-
if client_name in cls._message_handlers and not cls._is_shutdown:
|
|
669
|
-
asyncio.create_task(cls.start_consumer(client_name))
|
|
670
|
-
|
|
671
|
-
task.add_done_callback(task_done_callback)
|
|
672
|
-
logger.info(f"消费者任务 '{client_name}' 已创建")
|
|
673
|
-
|
|
674
|
-
@classmethod
|
|
675
|
-
async def default_message_handler(cls, parsed_data: MQMsgModel, original_message: AbstractIncomingMessage) -> None:
|
|
676
|
-
"""默认消息处理器"""
|
|
677
|
-
logger.info(f"\n===== 收到消息 [{original_message.routing_key}] =====")
|
|
678
|
-
logger.info(f"关联ID: {parsed_data.correlationDataId}")
|
|
679
|
-
logger.info(f"主题代码: {parsed_data.topicCode}")
|
|
680
|
-
logger.info(f"消息内容: {parsed_data.msg}")
|
|
681
|
-
logger.info("===================\n")
|
|
682
|
-
|
|
683
|
-
@classmethod
|
|
684
|
-
async def get_sender(cls, queue_name: str) -> Optional[RabbitMQClient]:
|
|
685
|
-
"""获取发送客户端(适配单通道池)"""
|
|
686
|
-
if cls._is_shutdown:
|
|
687
|
-
logger.warning("服务已关闭,无法获取发送器")
|
|
688
|
-
return None
|
|
689
|
-
|
|
690
|
-
if not queue_name:
|
|
691
|
-
logger.warning("发送器名称不能为空")
|
|
692
|
-
return None
|
|
693
|
-
|
|
694
|
-
# 检查是否在已注册的发送器中
|
|
695
|
-
if queue_name in cls._sender_client_names and queue_name in cls._clients:
|
|
696
|
-
client = cls._clients[queue_name]
|
|
697
|
-
if await client.is_connected:
|
|
698
|
-
return client
|
|
699
|
-
else:
|
|
700
|
-
logger.info(f"发送器 '{queue_name}' 连接已断开,尝试重连")
|
|
701
|
-
try:
|
|
702
|
-
await client.connect()
|
|
703
|
-
if await client.is_connected:
|
|
704
|
-
return client
|
|
705
|
-
except Exception as e:
|
|
706
|
-
logger.error(f"发送器 '{queue_name}' 重连失败: {str(e)}")
|
|
707
|
-
return None
|
|
708
|
-
|
|
709
|
-
# 检查是否带有app-name后缀
|
|
710
|
-
app_name = cls._config.get("APP_NAME", "") if cls._config else ""
|
|
711
|
-
if app_name:
|
|
712
|
-
suffixed_name = f"{queue_name}.{app_name}"
|
|
713
|
-
if suffixed_name in cls._sender_client_names and suffixed_name in cls._clients:
|
|
714
|
-
client = cls._clients[suffixed_name]
|
|
715
|
-
if await client.is_connected:
|
|
716
|
-
return client
|
|
717
|
-
else:
|
|
718
|
-
logger.info(f"发送器 '{suffixed_name}' 连接已断开,尝试重连")
|
|
719
|
-
try:
|
|
720
|
-
await client.connect()
|
|
721
|
-
if await client.is_connected:
|
|
722
|
-
return client
|
|
723
|
-
except Exception as e:
|
|
724
|
-
logger.error(f"发送器 '{suffixed_name}' 重连失败: {str(e)}")
|
|
725
|
-
|
|
726
|
-
logger.info(f"未找到可用的发送器 '{queue_name}'")
|
|
727
|
-
return None
|
|
728
|
-
|
|
729
|
-
@classmethod
|
|
730
|
-
async def send_message(
|
|
731
|
-
cls,
|
|
732
|
-
data: Union[BaseModel, str, Dict[str, Any], None],
|
|
733
|
-
queue_name: str, **kwargs
|
|
734
|
-
) -> None:
|
|
735
|
-
"""发送消息到指定队列(单通道场景下快速发送)"""
|
|
736
|
-
if cls._is_shutdown:
|
|
737
|
-
raise RuntimeError("RabbitMQService已关闭,无法发送消息")
|
|
738
|
-
|
|
739
|
-
# 获取发送客户端
|
|
740
|
-
sender = await cls.get_sender(queue_name)
|
|
741
|
-
if not sender:
|
|
742
|
-
error_msg = f"未找到可用的RabbitMQ发送器 (queue_name: {queue_name})"
|
|
743
|
-
logger.error(error_msg)
|
|
744
|
-
raise ValueError(error_msg)
|
|
745
|
-
|
|
746
|
-
# 确保连接有效
|
|
747
|
-
if not await sender.is_connected:
|
|
748
|
-
logger.info(f"发送器 '{queue_name}' 连接已关闭,尝试重新连接")
|
|
749
|
-
max_retry = 3
|
|
750
|
-
retry_count = 0
|
|
751
|
-
last_exception = None
|
|
752
|
-
|
|
753
|
-
while retry_count < max_retry and not cls._is_shutdown:
|
|
754
|
-
try:
|
|
755
|
-
await sender.connect()
|
|
756
|
-
if await sender.is_connected:
|
|
757
|
-
logger.info(
|
|
758
|
-
f"发送器 '{queue_name}' 第 {retry_count + 1} 次重连成功")
|
|
759
|
-
break
|
|
760
|
-
except Exception as e:
|
|
761
|
-
last_exception = e
|
|
762
|
-
retry_count += 1
|
|
763
|
-
logger.warning(
|
|
764
|
-
f"发送器 '{queue_name}' 第 {retry_count} 次重连失败: {str(e)}")
|
|
765
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
766
|
-
|
|
767
|
-
if retry_count >= max_retry and not await sender.is_connected:
|
|
768
|
-
error_msg = f"发送器 '{queue_name}' 经过 {max_retry} 次重连仍失败"
|
|
769
|
-
logger.error(f"{error_msg}: {str(last_exception)}")
|
|
770
|
-
raise Exception(error_msg) from last_exception
|
|
771
|
-
|
|
772
|
-
try:
|
|
773
|
-
# 处理消息数据
|
|
774
|
-
msg_content = ""
|
|
775
|
-
if isinstance(data, str):
|
|
776
|
-
msg_content = data
|
|
777
|
-
elif isinstance(data, BaseModel):
|
|
778
|
-
msg_content = data.model_dump_json()
|
|
779
|
-
elif isinstance(data, dict):
|
|
780
|
-
msg_content = json.dumps(data, ensure_ascii=False)
|
|
781
|
-
|
|
782
|
-
# 创建标准消息模型
|
|
783
|
-
mq_message = MQMsgModel(
|
|
784
|
-
topicCode=queue_name.split('.')[0] if queue_name else "",
|
|
785
|
-
msg=msg_content,
|
|
786
|
-
correlationDataId=kwargs.get(
|
|
787
|
-
'correlationDataId', logger.get_trace_id()),
|
|
788
|
-
groupId=kwargs.get('groupId', ''),
|
|
789
|
-
dataKey=kwargs.get('dataKey', ""),
|
|
790
|
-
manualFlag=kwargs.get('manualFlag', False),
|
|
791
|
-
traceId=logger.get_trace_id()
|
|
792
|
-
)
|
|
793
|
-
|
|
794
|
-
# 构建消息头
|
|
795
|
-
mq_header = {
|
|
796
|
-
"context": SsoUser(
|
|
797
|
-
tenant_id="T000002",
|
|
798
|
-
customer_id="SYSTEM",
|
|
799
|
-
user_id="SYSTEM",
|
|
800
|
-
user_name="SYSTEM",
|
|
801
|
-
request_path="",
|
|
802
|
-
req_type="SYSTEM",
|
|
803
|
-
trace_id=logger.get_trace_id(),
|
|
804
|
-
).model_dump_json()
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
# 发送消息(单通道场景下依赖原生异步确认)
|
|
808
|
-
await sender.publish(
|
|
809
|
-
message_body=mq_message.model_dump_json(),
|
|
810
|
-
headers=mq_header,
|
|
811
|
-
content_type="application/json"
|
|
812
|
-
)
|
|
813
|
-
logger.info(f"消息发送成功 (队列: {queue_name})")
|
|
814
|
-
except Exception as e:
|
|
815
|
-
logger.error(f"消息发送失败: {str(e)}", exc_info=True)
|
|
816
|
-
raise
|
|
817
|
-
|
|
818
33
|
@classmethod
|
|
819
34
|
async def shutdown(cls, timeout: float = 15.0) -> None:
|
|
820
|
-
"""
|
|
35
|
+
"""优雅关闭所有资源(保持原有接口)"""
|
|
821
36
|
async with cls._shutdown_lock:
|
|
822
37
|
if cls._is_shutdown:
|
|
823
38
|
logger.info("RabbitMQService已关闭,无需重复操作")
|
|
@@ -827,51 +42,18 @@ class RabbitMQService:
|
|
|
827
42
|
logger.info("开始关闭RabbitMQ服务...")
|
|
828
43
|
|
|
829
44
|
# 1. 停止连接监控任务
|
|
830
|
-
|
|
831
|
-
cls._connection_monitor_task.cancel()
|
|
832
|
-
try:
|
|
833
|
-
await asyncio.wait_for(cls._connection_monitor_task, timeout=timeout)
|
|
834
|
-
except asyncio.TimeoutError:
|
|
835
|
-
logger.warning("连接监控任务关闭超时")
|
|
836
|
-
except Exception as e:
|
|
837
|
-
logger.error(f"关闭连接监控任务失败: {str(e)}")
|
|
45
|
+
await cls.stop_connection_monitor(timeout)
|
|
838
46
|
|
|
839
47
|
# 2. 停止所有消费者任务
|
|
840
|
-
|
|
841
|
-
if not task.done():
|
|
842
|
-
# 触发停止事件
|
|
843
|
-
if client_name in cls._consumer_events:
|
|
844
|
-
cls._consumer_events[client_name].set()
|
|
845
|
-
# 取消任务
|
|
846
|
-
task.cancel()
|
|
847
|
-
try:
|
|
848
|
-
await asyncio.wait_for(task, timeout=timeout)
|
|
849
|
-
except Exception as e:
|
|
850
|
-
logger.error(f"关闭消费者 '{client_name}' 失败: {str(e)}")
|
|
48
|
+
await cls.shutdown_consumers(timeout)
|
|
851
49
|
|
|
852
|
-
# 3.
|
|
853
|
-
|
|
854
|
-
try:
|
|
855
|
-
await client.close()
|
|
856
|
-
except Exception as e:
|
|
857
|
-
logger.error(f"关闭客户端失败: {str(e)}")
|
|
50
|
+
# 3. 关闭所有客户端
|
|
51
|
+
await cls.shutdown_clients(timeout)
|
|
858
52
|
|
|
859
|
-
# 4.
|
|
860
|
-
|
|
861
|
-
try:
|
|
862
|
-
await cls._connection_pool.close()
|
|
863
|
-
logger.info("RabbitMQ单通道连接池已关闭")
|
|
864
|
-
except Exception as e:
|
|
865
|
-
logger.error(f"关闭连接池失败: {str(e)}")
|
|
53
|
+
# 4. 关闭连接池
|
|
54
|
+
await cls.shutdown_core_resources(timeout)
|
|
866
55
|
|
|
867
|
-
# 5.
|
|
868
|
-
cls.
|
|
869
|
-
cls._message_handlers.clear()
|
|
870
|
-
cls._consumer_tasks.clear()
|
|
871
|
-
cls._consumer_events.clear()
|
|
872
|
-
cls._consumer_tags.clear()
|
|
873
|
-
cls._sender_client_names.clear()
|
|
874
|
-
cls._init_locks.clear()
|
|
875
|
-
cls._config = None
|
|
56
|
+
# 5. 清理剩余状态
|
|
57
|
+
cls.clear_senders()
|
|
876
58
|
|
|
877
59
|
logger.info("RabbitMQService已完全关闭")
|