sycommon-python-lib 0.1.56__py3-none-any.whl → 0.1.56b2__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 +3 -24
- sycommon/llm/embedding.py +23 -78
- sycommon/llm/get_llm.py +164 -24
- sycommon/logging/kafka_log.py +433 -187
- sycommon/middleware/exception.py +16 -10
- sycommon/middleware/timeout.py +1 -2
- sycommon/middleware/traceid.py +76 -81
- sycommon/rabbitmq/rabbitmq_client.py +242 -232
- sycommon/rabbitmq/rabbitmq_pool.py +218 -278
- sycommon/rabbitmq/rabbitmq_service.py +843 -25
- sycommon/services.py +96 -122
- sycommon/synacos/nacos_service.py +779 -63
- sycommon/tools/merge_headers.py +0 -20
- sycommon/tools/snowflake.py +153 -101
- {sycommon_python_lib-0.1.56.dist-info → sycommon_python_lib-0.1.56b2.dist-info}/METADATA +8 -10
- {sycommon_python_lib-0.1.56.dist-info → sycommon_python_lib-0.1.56b2.dist-info}/RECORD +19 -40
- sycommon/config/LangfuseConfig.py +0 -15
- sycommon/config/SentryConfig.py +0 -13
- sycommon/llm/llm_tokens.py +0 -119
- sycommon/llm/struct_token.py +0 -192
- sycommon/llm/sy_langfuse.py +0 -103
- sycommon/llm/usage_token.py +0 -117
- sycommon/notice/__init__.py +0 -0
- sycommon/notice/uvicorn_monitor.py +0 -200
- sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -206
- sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -73
- sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -285
- sycommon/rabbitmq/rabbitmq_service_core.py +0 -117
- sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -238
- sycommon/sentry/__init__.py +0 -0
- sycommon/sentry/sy_sentry.py +0 -35
- sycommon/synacos/nacos_client_base.py +0 -119
- sycommon/synacos/nacos_config_manager.py +0 -107
- sycommon/synacos/nacos_heartbeat_manager.py +0 -144
- sycommon/synacos/nacos_service_discovery.py +0 -157
- sycommon/synacos/nacos_service_registration.py +0 -270
- sycommon/tools/env.py +0 -62
- {sycommon_python_lib-0.1.56.dist-info → sycommon_python_lib-0.1.56b2.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56.dist-info → sycommon_python_lib-0.1.56b2.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56.dist-info → sycommon_python_lib-0.1.56b2.dist-info}/top_level.txt +0 -0
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
import sys
|
|
3
|
-
import traceback
|
|
4
|
-
import aiohttp
|
|
5
|
-
import asyncio
|
|
6
|
-
import json
|
|
7
|
-
from typing import Optional
|
|
8
|
-
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
|
|
9
|
-
from sycommon.config.Config import Config
|
|
10
|
-
from sycommon.logging.kafka_log import SYLogger
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def send_wechat_markdown_msg(
|
|
14
|
-
content: str,
|
|
15
|
-
webhook: str = None
|
|
16
|
-
) -> Optional[dict]:
|
|
17
|
-
"""
|
|
18
|
-
异步发送企业微信Markdown格式的WebHook消息
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
content: Markdown格式的消息内容(支持企业微信支持的markdown语法)
|
|
22
|
-
webhook: 完整的企业微信WebHook URL(默认值包含key)
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
接口返回的JSON数据(dict),失败返回None
|
|
26
|
-
"""
|
|
27
|
-
# 设置请求头
|
|
28
|
-
headers = {
|
|
29
|
-
"Content-Type": "application/json; charset=utf-8"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# 构造请求体(Markdown格式)
|
|
33
|
-
payload = {
|
|
34
|
-
"msgtype": "markdown",
|
|
35
|
-
"markdown": {
|
|
36
|
-
"content": content
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try:
|
|
41
|
-
async with aiohttp.ClientSession() as session:
|
|
42
|
-
async with session.post(
|
|
43
|
-
url=webhook,
|
|
44
|
-
data=json.dumps(payload, ensure_ascii=False),
|
|
45
|
-
headers=headers,
|
|
46
|
-
timeout=aiohttp.ClientTimeout(total=10)
|
|
47
|
-
) as response:
|
|
48
|
-
status = response.status
|
|
49
|
-
response_text = await response.text()
|
|
50
|
-
response_data = json.loads(
|
|
51
|
-
response_text) if response_text else {}
|
|
52
|
-
|
|
53
|
-
if status == 200 and response_data.get("errcode") == 0:
|
|
54
|
-
SYLogger.info(f"消息发送成功: {response_data}")
|
|
55
|
-
return response_data
|
|
56
|
-
else:
|
|
57
|
-
SYLogger.info(
|
|
58
|
-
f"消息发送失败 - 状态码: {status}, 响应: {response_data}")
|
|
59
|
-
return None
|
|
60
|
-
except Exception as e:
|
|
61
|
-
SYLogger.info(f"错误:未知异常 - {str(e)}")
|
|
62
|
-
return None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async def send_webhook(error_info: dict = None, webhook: str = None):
|
|
66
|
-
"""
|
|
67
|
-
发送服务启动结果的企业微信通知
|
|
68
|
-
Args:
|
|
69
|
-
error_info: 错误信息字典(启动失败时必填),包含:error_type, error_msg, stack_trace, elapsed_time
|
|
70
|
-
webhook: 完整的企业微信WebHook URL(覆盖默认值)
|
|
71
|
-
"""
|
|
72
|
-
# 获取服务名和环境(兼容配置读取失败)
|
|
73
|
-
try:
|
|
74
|
-
service_name = Config().config.get('Name', "未知服务")
|
|
75
|
-
env = Config().config.get('Nacos', {}).get('namespaceId', '未知环境')
|
|
76
|
-
webHook = Config().config.get('llm', {}).get('WebHook', '未知环境')
|
|
77
|
-
except Exception as e:
|
|
78
|
-
service_name = "未知服务"
|
|
79
|
-
env = "未知环境"
|
|
80
|
-
webHook = None
|
|
81
|
-
SYLogger.info(f"读取配置失败: {str(e)}")
|
|
82
|
-
|
|
83
|
-
start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
84
|
-
|
|
85
|
-
# 启动失败的通知内容(包含详细错误信息)
|
|
86
|
-
error_type = error_info.get("error_type", "未知错误")
|
|
87
|
-
error_msg = error_info.get("error_msg", "无错误信息")
|
|
88
|
-
stack_trace = error_info.get("stack_trace", "无堆栈信息")[:500] # 限制长度避免超限
|
|
89
|
-
elapsed_time = error_info.get("elapsed_time", 0)
|
|
90
|
-
|
|
91
|
-
markdown_content = f"""### {service_name}服务启动失败告警 ⚠️
|
|
92
|
-
> 环境: <font color="warning">{env}</font>
|
|
93
|
-
> 启动时间: <font color="comment">{start_time}</font>
|
|
94
|
-
> 耗时: <font color="comment">{elapsed_time:.2f}秒</font>
|
|
95
|
-
> 错误类型: <font color="danger">{error_type}</font>
|
|
96
|
-
> 错误信息: <font color="danger">{error_msg}</font>
|
|
97
|
-
> 错误堆栈: {stack_trace}"""
|
|
98
|
-
|
|
99
|
-
if webhook or webHook:
|
|
100
|
-
result = await send_wechat_markdown_msg(
|
|
101
|
-
content=markdown_content,
|
|
102
|
-
webhook=webhook or webHook
|
|
103
|
-
)
|
|
104
|
-
SYLogger.info(f"通知发送结果: {result}")
|
|
105
|
-
else:
|
|
106
|
-
SYLogger.info("未设置企业微信WebHook")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def run(*args, webhook: str = None, **kwargs):
|
|
110
|
-
"""
|
|
111
|
-
带企业微信告警的Uvicorn启动监控
|
|
112
|
-
调用方式1(默认配置):uvicorn_monitor.run("app:app", **app.state.config)
|
|
113
|
-
调用方式2(自定义webhook):uvicorn_monitor.run("app:app", webhook="完整URL", **app.state.config)
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
*args: 传递给uvicorn.run的位置参数(如"app:app")
|
|
117
|
-
webhook: 完整的企业微信WebHook URL(可选,覆盖默认值)
|
|
118
|
-
**kwargs: 传递给uvicorn.run的关键字参数(如app.state.config)
|
|
119
|
-
"""
|
|
120
|
-
# 判断环境
|
|
121
|
-
env = Config().config.get('Nacos', {}).get('namespaceId', '未知环境')
|
|
122
|
-
if env == "prod":
|
|
123
|
-
import uvicorn
|
|
124
|
-
uvicorn.run(*args, **kwargs)
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
# 记录启动开始时间
|
|
128
|
-
start_time = datetime.now()
|
|
129
|
-
|
|
130
|
-
if webhook:
|
|
131
|
-
# 脱敏展示webhook(隐藏key的后半部分)
|
|
132
|
-
parsed = urlparse(webhook)
|
|
133
|
-
query = parse_qs(parsed.query)
|
|
134
|
-
if 'key' in query and query['key'][0]:
|
|
135
|
-
key = query['key'][0]
|
|
136
|
-
masked_key = key[:8] + "****" if len(key) > 8 else key + "****"
|
|
137
|
-
query['key'] = [masked_key]
|
|
138
|
-
masked_query = urlencode(query, doseq=True)
|
|
139
|
-
masked_webhook = urlunparse(
|
|
140
|
-
(parsed.scheme, parsed.netloc, parsed.path, parsed.params, masked_query, parsed.fragment))
|
|
141
|
-
SYLogger.info(f"自定义企业微信WebHook: {masked_webhook}")
|
|
142
|
-
|
|
143
|
-
# 初始化错误信息
|
|
144
|
-
error_info = None
|
|
145
|
-
|
|
146
|
-
try:
|
|
147
|
-
import uvicorn
|
|
148
|
-
# 执行启动(如果启动成功,此方法会阻塞,不会执行后续except)
|
|
149
|
-
uvicorn.run(*args, **kwargs)
|
|
150
|
-
|
|
151
|
-
except KeyboardInterrupt:
|
|
152
|
-
# 处理用户手动中断(不算启动失败)
|
|
153
|
-
elapsed = (datetime.now() - start_time).total_seconds()
|
|
154
|
-
SYLogger.info(f"\n{'='*50}")
|
|
155
|
-
SYLogger.info(f"ℹ️ 应用被用户手动中断")
|
|
156
|
-
SYLogger.info(f"启动耗时: {elapsed:.2f} 秒")
|
|
157
|
-
SYLogger.info(f"{'='*50}\n")
|
|
158
|
-
sys.exit(0)
|
|
159
|
-
|
|
160
|
-
except Exception as e:
|
|
161
|
-
# 捕获启动失败异常,收集错误信息
|
|
162
|
-
elapsed = (datetime.now() - start_time).total_seconds()
|
|
163
|
-
# 捕获堆栈信息
|
|
164
|
-
stack_trace = traceback.format_exc()
|
|
165
|
-
|
|
166
|
-
# 构造错误信息字典
|
|
167
|
-
error_info = {
|
|
168
|
-
"error_type": type(e).__name__,
|
|
169
|
-
"error_msg": str(e),
|
|
170
|
-
"stack_trace": stack_trace,
|
|
171
|
-
"elapsed_time": elapsed
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
# 打印错误信息
|
|
175
|
-
SYLogger.info(f"\n{'='*50}")
|
|
176
|
-
SYLogger.info(f"🚨 应用启动失败!")
|
|
177
|
-
SYLogger.info(f"失败时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
178
|
-
SYLogger.info(f"错误类型: {type(e).__name__}")
|
|
179
|
-
SYLogger.info(f"错误信息: {str(e)}")
|
|
180
|
-
SYLogger.info(f"\n📝 错误堆栈(关键):")
|
|
181
|
-
SYLogger.info(f"-"*50)
|
|
182
|
-
traceback.print_exc(file=sys.stdout)
|
|
183
|
-
SYLogger.info(f"\n⏱️ 启动耗时: {elapsed:.2f} 秒")
|
|
184
|
-
SYLogger.info(f"{'='*50}\n")
|
|
185
|
-
|
|
186
|
-
finally:
|
|
187
|
-
# 运行异步通知函数,传递自定义的webhook参数
|
|
188
|
-
try:
|
|
189
|
-
asyncio.run(send_webhook(
|
|
190
|
-
error_info=error_info,
|
|
191
|
-
webhook=webhook
|
|
192
|
-
))
|
|
193
|
-
except Exception as e:
|
|
194
|
-
SYLogger.info(f"错误:异步通知失败 - {str(e)}")
|
|
195
|
-
# 启动失败时退出程序
|
|
196
|
-
sys.exit(1)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
# 兼容旧调用方式(可选)
|
|
200
|
-
run_uvicorn_with_monitor = run
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
import asyncio
|
|
3
|
-
|
|
4
|
-
from sycommon.logging.kafka_log import SYLogger
|
|
5
|
-
from sycommon.rabbitmq.rabbitmq_client import RabbitMQClient
|
|
6
|
-
from sycommon.rabbitmq.rabbitmq_service_core import RabbitMQCoreService
|
|
7
|
-
|
|
8
|
-
logger = SYLogger
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class RabbitMQClientManager(RabbitMQCoreService):
|
|
12
|
-
"""
|
|
13
|
-
RabbitMQ客户端管理类 - 负责客户端的创建、获取、重连、资源清理
|
|
14
|
-
"""
|
|
15
|
-
# 客户端存储
|
|
16
|
-
_clients: Dict[str, RabbitMQClient] = {}
|
|
17
|
-
_init_locks: Dict[str, asyncio.Lock] = {}
|
|
18
|
-
|
|
19
|
-
# 模式标记
|
|
20
|
-
_has_listeners: bool = False
|
|
21
|
-
_has_senders: bool = False
|
|
22
|
-
|
|
23
|
-
@classmethod
|
|
24
|
-
def set_mode_flags(cls, has_listeners: bool = False, has_senders: bool = False) -> None:
|
|
25
|
-
"""设置模式标记(是否有监听器/发送器)"""
|
|
26
|
-
cls._has_listeners = has_listeners
|
|
27
|
-
cls._has_senders = has_senders
|
|
28
|
-
|
|
29
|
-
@classmethod
|
|
30
|
-
async def _clean_client_resources(cls, client: RabbitMQClient) -> None:
|
|
31
|
-
"""清理客户端无效资源"""
|
|
32
|
-
try:
|
|
33
|
-
# 先停止消费
|
|
34
|
-
if client._consumer_tag:
|
|
35
|
-
await client.stop_consuming()
|
|
36
|
-
logger.debug("客户端无效资源清理完成(单通道无需归还)")
|
|
37
|
-
except Exception as e:
|
|
38
|
-
logger.warning(f"释放客户端无效资源失败: {str(e)}")
|
|
39
|
-
finally:
|
|
40
|
-
# 强制重置客户端状态
|
|
41
|
-
client._channel = None
|
|
42
|
-
client._channel_conn = None
|
|
43
|
-
client._exchange = None
|
|
44
|
-
client._queue = None
|
|
45
|
-
client._consumer_tag = None
|
|
46
|
-
|
|
47
|
-
@classmethod
|
|
48
|
-
async def _reconnect_client(cls, client_name: str, client: RabbitMQClient) -> bool:
|
|
49
|
-
"""客户端重连"""
|
|
50
|
-
if cls._is_shutdown or not (cls._connection_pool and await cls._connection_pool.is_alive):
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
# 重连冷却
|
|
54
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
55
|
-
|
|
56
|
-
try:
|
|
57
|
-
# 清理旧资源
|
|
58
|
-
await cls._clean_client_resources(client)
|
|
59
|
-
|
|
60
|
-
# 执行重连
|
|
61
|
-
await client.connect()
|
|
62
|
-
|
|
63
|
-
# 验证重连结果
|
|
64
|
-
if await client.is_connected:
|
|
65
|
-
logger.info(f"客户端 '{client_name}' 重连成功")
|
|
66
|
-
return True
|
|
67
|
-
else:
|
|
68
|
-
logger.warning(f"客户端 '{client_name}' 重连失败:资源未完全初始化")
|
|
69
|
-
return False
|
|
70
|
-
except Exception as e:
|
|
71
|
-
logger.error(f"客户端 '{client_name}' 重连失败: {str(e)}", exc_info=True)
|
|
72
|
-
return False
|
|
73
|
-
|
|
74
|
-
@classmethod
|
|
75
|
-
async def _create_client(cls, **kwargs) -> RabbitMQClient:
|
|
76
|
-
"""创建客户端实例"""
|
|
77
|
-
if cls._is_shutdown:
|
|
78
|
-
raise RuntimeError("RabbitMQService已关闭,无法创建客户端")
|
|
79
|
-
|
|
80
|
-
# 等待连接池就绪
|
|
81
|
-
await cls.wait_for_pool_ready()
|
|
82
|
-
|
|
83
|
-
app_name = kwargs.get('app_name', cls._config.get(
|
|
84
|
-
"APP_NAME", "")) if cls._config else ""
|
|
85
|
-
queue_name = kwargs.get('queue_name', '')
|
|
86
|
-
|
|
87
|
-
create_if_not_exists = kwargs.get('create_if_not_exists', True)
|
|
88
|
-
|
|
89
|
-
processed_queue_name = queue_name
|
|
90
|
-
if create_if_not_exists and processed_queue_name and app_name:
|
|
91
|
-
if not processed_queue_name.endswith(f".{app_name}"):
|
|
92
|
-
processed_queue_name = f"{processed_queue_name}.{app_name}"
|
|
93
|
-
logger.info(f"监听器队列名称自动拼接app-name: {processed_queue_name}")
|
|
94
|
-
else:
|
|
95
|
-
logger.info(f"监听器队列已包含app-name: {processed_queue_name}")
|
|
96
|
-
|
|
97
|
-
logger.info(
|
|
98
|
-
f"创建客户端 - 原始队列名: {queue_name}, "
|
|
99
|
-
f"处理后队列名: {processed_queue_name}, "
|
|
100
|
-
f"是否创建队列: {create_if_not_exists}"
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
# 创建客户端实例
|
|
104
|
-
client = RabbitMQClient(
|
|
105
|
-
connection_pool=cls._connection_pool,
|
|
106
|
-
exchange_name=cls._config.get(
|
|
107
|
-
'exchange_name', "system.topic.exchange"),
|
|
108
|
-
exchange_type=kwargs.get('exchange_type', "topic"),
|
|
109
|
-
queue_name=None if not create_if_not_exists else processed_queue_name,
|
|
110
|
-
routing_key=kwargs.get(
|
|
111
|
-
'routing_key',
|
|
112
|
-
f"{queue_name.split('.')[0]}.#"
|
|
113
|
-
),
|
|
114
|
-
durable=kwargs.get('durable', True),
|
|
115
|
-
auto_delete=kwargs.get('auto_delete', False),
|
|
116
|
-
auto_parse_json=kwargs.get('auto_parse_json', True),
|
|
117
|
-
create_if_not_exists=create_if_not_exists,
|
|
118
|
-
prefetch_count=kwargs.get('prefetch_count', 2),
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
# 连接客户端
|
|
122
|
-
await client.connect()
|
|
123
|
-
|
|
124
|
-
return client
|
|
125
|
-
|
|
126
|
-
@classmethod
|
|
127
|
-
async def get_client(
|
|
128
|
-
cls,
|
|
129
|
-
client_name: str = "default",
|
|
130
|
-
client_type: str = "sender", # sender(发送器)/listener(监听器),默认sender
|
|
131
|
-
**kwargs
|
|
132
|
-
) -> RabbitMQClient:
|
|
133
|
-
"""
|
|
134
|
-
获取或创建RabbitMQ客户端
|
|
135
|
-
:param client_name: 客户端名称
|
|
136
|
-
:param client_type: 客户端类型 - sender(发送器)/listener(监听器)
|
|
137
|
-
:param kwargs: 其他参数
|
|
138
|
-
:return: RabbitMQClient实例
|
|
139
|
-
"""
|
|
140
|
-
if cls._is_shutdown:
|
|
141
|
-
raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
|
|
142
|
-
|
|
143
|
-
# 校验client_type合法性
|
|
144
|
-
if client_type not in ["sender", "listener"]:
|
|
145
|
-
raise ValueError(
|
|
146
|
-
f"client_type只能是sender/listener,当前值:{client_type}")
|
|
147
|
-
|
|
148
|
-
# 等待连接池就绪
|
|
149
|
-
await cls.wait_for_pool_ready()
|
|
150
|
-
|
|
151
|
-
# 确保锁存在
|
|
152
|
-
if client_name not in cls._init_locks:
|
|
153
|
-
cls._init_locks[client_name] = asyncio.Lock()
|
|
154
|
-
|
|
155
|
-
async with cls._init_locks[client_name]:
|
|
156
|
-
# ===== 原有“客户端已存在”的逻辑保留 =====
|
|
157
|
-
if client_name in cls._clients:
|
|
158
|
-
client = cls._clients[client_name]
|
|
159
|
-
# 核心:根据client_type重置客户端的队列创建配置
|
|
160
|
-
if client_type == "sender":
|
|
161
|
-
client.create_if_not_exists = False
|
|
162
|
-
else: # listener
|
|
163
|
-
client.create_if_not_exists = True
|
|
164
|
-
# 监听器必须有队列名,从kwargs补全
|
|
165
|
-
if not client.queue_name and kwargs.get("queue_name"):
|
|
166
|
-
client.queue_name = kwargs.get("queue_name")
|
|
167
|
-
|
|
168
|
-
if await client.is_connected:
|
|
169
|
-
return client
|
|
170
|
-
else:
|
|
171
|
-
logger.info(f"客户端 '{client_name}' 连接已断开,重新创建")
|
|
172
|
-
await cls._clean_client_resources(client)
|
|
173
|
-
|
|
174
|
-
# ===== 核心逻辑:根据client_type统一控制队列创建 =====
|
|
175
|
-
if client_type == "sender":
|
|
176
|
-
kwargs["create_if_not_exists"] = False # 禁用创建队列
|
|
177
|
-
else:
|
|
178
|
-
if not kwargs.get("queue_name"):
|
|
179
|
-
raise ValueError("监听器类型必须指定queue_name参数")
|
|
180
|
-
kwargs["create_if_not_exists"] = True
|
|
181
|
-
|
|
182
|
-
client = await cls._create_client(
|
|
183
|
-
**kwargs
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
# 监听器额外验证队列创建结果
|
|
187
|
-
if client_type == "listener" and not client._queue:
|
|
188
|
-
raise RuntimeError(f"监听器队列 '{kwargs['queue_name']}' 创建失败")
|
|
189
|
-
|
|
190
|
-
# 存储客户端
|
|
191
|
-
cls._clients[client_name] = client
|
|
192
|
-
return client
|
|
193
|
-
|
|
194
|
-
@classmethod
|
|
195
|
-
async def shutdown_clients(cls, timeout: float = 15.0) -> None:
|
|
196
|
-
"""关闭所有客户端"""
|
|
197
|
-
# 关闭所有客户端
|
|
198
|
-
for client in cls._clients.values():
|
|
199
|
-
try:
|
|
200
|
-
await client.close()
|
|
201
|
-
except Exception as e:
|
|
202
|
-
logger.error(f"关闭客户端失败: {str(e)}")
|
|
203
|
-
|
|
204
|
-
# 清理客户端状态
|
|
205
|
-
cls._clients.clear()
|
|
206
|
-
cls._init_locks.clear()
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from typing import Optional
|
|
3
|
-
from sycommon.rabbitmq.rabbitmq_service_client_manager import RabbitMQClientManager
|
|
4
|
-
from sycommon.logging.kafka_log import SYLogger
|
|
5
|
-
|
|
6
|
-
logger = SYLogger
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class RabbitMQConnectionMonitor(RabbitMQClientManager):
|
|
10
|
-
"""
|
|
11
|
-
RabbitMQ连接监控类 - 负责连接状态监控、自动重连
|
|
12
|
-
"""
|
|
13
|
-
_connection_monitor_task: Optional[asyncio.Task] = None
|
|
14
|
-
|
|
15
|
-
@classmethod
|
|
16
|
-
async def _monitor_connections(cls):
|
|
17
|
-
"""连接监控任务:定期检查所有客户端连接状态"""
|
|
18
|
-
logger.info("RabbitMQ连接监控任务启动")
|
|
19
|
-
while not cls._is_shutdown:
|
|
20
|
-
try:
|
|
21
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
22
|
-
|
|
23
|
-
# 跳过未初始化的连接池
|
|
24
|
-
if not cls._connection_pool or not cls._connection_pool._initialized:
|
|
25
|
-
continue
|
|
26
|
-
|
|
27
|
-
# 检查连接池本身状态
|
|
28
|
-
pool_alive = await cls._connection_pool.is_alive
|
|
29
|
-
if not pool_alive:
|
|
30
|
-
logger.error("RabbitMQ连接池已断开,等待原生自动重连")
|
|
31
|
-
continue
|
|
32
|
-
|
|
33
|
-
# 检查所有客户端连接
|
|
34
|
-
for client_name, client in list(cls._clients.items()):
|
|
35
|
-
try:
|
|
36
|
-
client_connected = await client.is_connected
|
|
37
|
-
if not client_connected:
|
|
38
|
-
logger.warning(
|
|
39
|
-
f"客户端 '{client_name}' 连接异常,触发重连")
|
|
40
|
-
asyncio.create_task(
|
|
41
|
-
cls._reconnect_client(client_name, client))
|
|
42
|
-
except Exception as e:
|
|
43
|
-
logger.error(
|
|
44
|
-
f"监控客户端 '{client_name}' 连接状态失败: {str(e)}", exc_info=True)
|
|
45
|
-
|
|
46
|
-
except Exception as e:
|
|
47
|
-
logger.error("RabbitMQ连接监控任务异常", exc_info=True)
|
|
48
|
-
await asyncio.sleep(cls.RECONNECT_INTERVAL)
|
|
49
|
-
|
|
50
|
-
logger.info("RabbitMQ连接监控任务停止")
|
|
51
|
-
|
|
52
|
-
@classmethod
|
|
53
|
-
def start_connection_monitor(cls) -> None:
|
|
54
|
-
"""启动连接监控任务"""
|
|
55
|
-
if cls._connection_monitor_task and not cls._connection_monitor_task.done():
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
cls._connection_monitor_task = asyncio.create_task(
|
|
59
|
-
cls._monitor_connections())
|
|
60
|
-
|
|
61
|
-
@classmethod
|
|
62
|
-
async def stop_connection_monitor(cls, timeout: float = 15.0) -> None:
|
|
63
|
-
"""停止连接监控任务"""
|
|
64
|
-
if cls._connection_monitor_task and not cls._connection_monitor_task.done():
|
|
65
|
-
cls._connection_monitor_task.cancel()
|
|
66
|
-
try:
|
|
67
|
-
await asyncio.wait_for(cls._connection_monitor_task, timeout=timeout)
|
|
68
|
-
except asyncio.TimeoutError:
|
|
69
|
-
logger.warning("连接监控任务关闭超时")
|
|
70
|
-
except Exception as e:
|
|
71
|
-
logger.error(f"关闭连接监控任务失败: {str(e)}")
|
|
72
|
-
|
|
73
|
-
cls._connection_monitor_task = None
|