sycommon-python-lib 0.1.56b7__tar.gz → 0.1.56b9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/PKG-INFO +1 -1
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/pyproject.toml +1 -1
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/Config.py +1 -3
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/traceid.py +19 -20
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service.py +59 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +212 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +73 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +283 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service_core.py +117 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +235 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_client_base.py +119 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_config_manager.py +106 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_heartbeat_manager.py +142 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_service.py +150 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_service_discovery.py +138 -0
- sycommon_python_lib-0.1.56b9/src/sycommon/synacos/nacos_service_registration.py +252 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/SOURCES.txt +10 -0
- sycommon_python_lib-0.1.56b7/src/sycommon/rabbitmq/rabbitmq_service.py +0 -888
- sycommon_python_lib-0.1.56b7/src/sycommon/synacos/nacos_service.py +0 -890
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/README.md +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/setup.cfg +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -15,10 +15,8 @@ class Config(metaclass=SingletonMeta):
|
|
|
15
15
|
with open(config_file, 'r', encoding='utf-8') as f:
|
|
16
16
|
self.config = yaml.safe_load(f)
|
|
17
17
|
self.MaxBytes = self.config.get('MaxBytes', 209715200)
|
|
18
|
-
self.Timeout = self.config.get('Timeout',
|
|
18
|
+
self.Timeout = self.config.get('Timeout', 600000)
|
|
19
19
|
self.MaxRetries = self.config.get('MaxRetries', 3)
|
|
20
|
-
self.OCR = self.config.get('OCR', None)
|
|
21
|
-
self.INVOICE_OCR = self.config.get('INVOICE_OCR', None)
|
|
22
20
|
self.llm_configs = []
|
|
23
21
|
self.embedding_configs = []
|
|
24
22
|
self.reranker_configs = []
|
{sycommon_python_lib-0.1.56b7 → sycommon_python_lib-0.1.56b9}/src/sycommon/middleware/traceid.py
RENAMED
|
@@ -11,8 +11,7 @@ def setup_trace_id_handler(app):
|
|
|
11
11
|
@app.middleware("http")
|
|
12
12
|
async def trace_id_and_log_middleware(request: Request, call_next):
|
|
13
13
|
# ========== 1. 请求阶段:获取/生成 TraceID ==========
|
|
14
|
-
trace_id = request.headers.get(
|
|
15
|
-
"x-traceId-header") or request.headers.get("x-traceid-header")
|
|
14
|
+
trace_id = request.headers.get("x-traceid-header")
|
|
16
15
|
if not trace_id:
|
|
17
16
|
trace_id = Snowflake.id
|
|
18
17
|
|
|
@@ -101,15 +100,15 @@ def setup_trace_id_handler(app):
|
|
|
101
100
|
# 处理 SSE (Server-Sent Events)
|
|
102
101
|
if "text/event-stream" in response_content_type:
|
|
103
102
|
try:
|
|
104
|
-
response.headers["x-
|
|
103
|
+
response.headers["x-traceid-header"] = trace_id
|
|
105
104
|
expose_headers = response.headers.get(
|
|
106
105
|
"access-control-expose-headers", "")
|
|
107
106
|
if expose_headers:
|
|
108
|
-
if "x-
|
|
107
|
+
if "x-traceid-header" not in expose_headers.lower():
|
|
109
108
|
response.headers[
|
|
110
|
-
"access-control-expose-headers"] = f"{expose_headers}, x-
|
|
109
|
+
"access-control-expose-headers"] = f"{expose_headers}, x-traceid-header"
|
|
111
110
|
else:
|
|
112
|
-
response.headers["access-control-expose-headers"] = "x-
|
|
111
|
+
response.headers["access-control-expose-headers"] = "x-traceid-header"
|
|
113
112
|
|
|
114
113
|
# SSE 必须移除 Content-Length
|
|
115
114
|
headers_lower = {
|
|
@@ -120,12 +119,12 @@ def setup_trace_id_handler(app):
|
|
|
120
119
|
# 流式响应头只读处理
|
|
121
120
|
new_headers = dict(response.headers) if hasattr(
|
|
122
121
|
response.headers, 'items') else {}
|
|
123
|
-
new_headers["x-
|
|
122
|
+
new_headers["x-traceid-header"] = trace_id
|
|
124
123
|
if "access-control-expose-headers" in new_headers:
|
|
125
|
-
if "x-
|
|
126
|
-
new_headers["access-control-expose-headers"] += ", x-
|
|
124
|
+
if "x-traceid-header" not in new_headers["access-control-expose-headers"].lower():
|
|
125
|
+
new_headers["access-control-expose-headers"] += ", x-traceid-header"
|
|
127
126
|
else:
|
|
128
|
-
new_headers["access-control-expose-headers"] = "x-
|
|
127
|
+
new_headers["access-control-expose-headers"] = "x-traceid-header"
|
|
129
128
|
new_headers.pop("content-length", None)
|
|
130
129
|
response.init_headers(new_headers)
|
|
131
130
|
|
|
@@ -158,18 +157,18 @@ def setup_trace_id_handler(app):
|
|
|
158
157
|
delete_keys={'content-length', 'accept', 'content-type'}
|
|
159
158
|
)
|
|
160
159
|
|
|
161
|
-
# 强制加入 x-
|
|
162
|
-
merged_headers["x-
|
|
160
|
+
# 强制加入 x-traceid-header
|
|
161
|
+
merged_headers["x-traceid-header"] = trace_id
|
|
163
162
|
merged_headers.update(cors_headers)
|
|
164
163
|
|
|
165
164
|
# 更新暴露头
|
|
166
165
|
expose_headers = merged_headers.get(
|
|
167
166
|
"access-control-expose-headers", "")
|
|
168
167
|
if expose_headers:
|
|
169
|
-
if "x-
|
|
170
|
-
merged_headers["access-control-expose-headers"] = f"{expose_headers}, x-
|
|
168
|
+
if "x-traceid-header" not in expose_headers.lower():
|
|
169
|
+
merged_headers["access-control-expose-headers"] = f"{expose_headers}, x-traceid-header"
|
|
171
170
|
else:
|
|
172
|
-
merged_headers["access-control-expose-headers"] = "x-
|
|
171
|
+
merged_headers["access-control-expose-headers"] = "x-traceid-header"
|
|
173
172
|
|
|
174
173
|
# 应用 Headers
|
|
175
174
|
if hasattr(response.headers, 'clear'):
|
|
@@ -212,7 +211,7 @@ def setup_trace_id_handler(app):
|
|
|
212
211
|
media_type=response.media_type
|
|
213
212
|
)
|
|
214
213
|
response.headers["content-length"] = str(len(new_body))
|
|
215
|
-
response.headers["x-
|
|
214
|
+
response.headers["x-traceid-header"] = trace_id
|
|
216
215
|
# 恢复 CORS
|
|
217
216
|
for k, v in cors_headers.items():
|
|
218
217
|
response.headers[k] = v
|
|
@@ -226,7 +225,7 @@ def setup_trace_id_handler(app):
|
|
|
226
225
|
)
|
|
227
226
|
response.headers["content-length"] = str(
|
|
228
227
|
len(response_body))
|
|
229
|
-
response.headers["x-
|
|
228
|
+
response.headers["x-traceid-header"] = trace_id
|
|
230
229
|
for k, v in cors_headers.items():
|
|
231
230
|
response.headers[k] = v
|
|
232
231
|
else:
|
|
@@ -239,7 +238,7 @@ def setup_trace_id_handler(app):
|
|
|
239
238
|
)
|
|
240
239
|
response.headers["content-length"] = str(
|
|
241
240
|
len(response_body))
|
|
242
|
-
response.headers["x-
|
|
241
|
+
response.headers["x-traceid-header"] = trace_id
|
|
243
242
|
for k, v in cors_headers.items():
|
|
244
243
|
response.headers[k] = v
|
|
245
244
|
except StopAsyncIteration:
|
|
@@ -255,11 +254,11 @@ def setup_trace_id_handler(app):
|
|
|
255
254
|
|
|
256
255
|
# 兜底:确保 Header 必有 TraceId
|
|
257
256
|
try:
|
|
258
|
-
response.headers["x-
|
|
257
|
+
response.headers["x-traceid-header"] = trace_id
|
|
259
258
|
except AttributeError:
|
|
260
259
|
new_headers = dict(response.headers) if hasattr(
|
|
261
260
|
response.headers, 'items') else {}
|
|
262
|
-
new_headers["x-
|
|
261
|
+
new_headers["x-traceid-header"] = trace_id
|
|
263
262
|
if hasattr(response, "init_headers"):
|
|
264
263
|
response.init_headers(new_headers)
|
|
265
264
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
import asyncio
|
|
3
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
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
|
|
7
|
+
|
|
8
|
+
logger = SYLogger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RabbitMQService(RabbitMQConnectionMonitor, RabbitMQProducerManager, RabbitMQConsumerManager):
|
|
12
|
+
"""
|
|
13
|
+
RabbitMQ服务对外统一接口 - 保持原有API兼容
|
|
14
|
+
"""
|
|
15
|
+
@classmethod
|
|
16
|
+
def init(cls, config: dict, has_listeners: bool = False, has_senders: bool = False) -> Type['RabbitMQService']:
|
|
17
|
+
"""初始化RabbitMQ服务(保持原有接口)"""
|
|
18
|
+
# 初始化配置
|
|
19
|
+
cls.init_config(config)
|
|
20
|
+
|
|
21
|
+
# 设置模式标记
|
|
22
|
+
cls.set_mode_flags(has_listeners=has_listeners,
|
|
23
|
+
has_senders=has_senders)
|
|
24
|
+
|
|
25
|
+
# 初始化连接池
|
|
26
|
+
asyncio.create_task(cls.init_connection_pool())
|
|
27
|
+
|
|
28
|
+
# 启动连接监控
|
|
29
|
+
cls.start_connection_monitor()
|
|
30
|
+
|
|
31
|
+
return cls
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
async def shutdown(cls, timeout: float = 15.0) -> None:
|
|
35
|
+
"""优雅关闭所有资源(保持原有接口)"""
|
|
36
|
+
async with cls._shutdown_lock:
|
|
37
|
+
if cls._is_shutdown:
|
|
38
|
+
logger.info("RabbitMQService已关闭,无需重复操作")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
cls._is_shutdown = True
|
|
42
|
+
logger.info("开始关闭RabbitMQ服务...")
|
|
43
|
+
|
|
44
|
+
# 1. 停止连接监控任务
|
|
45
|
+
await cls.stop_connection_monitor(timeout)
|
|
46
|
+
|
|
47
|
+
# 2. 停止所有消费者任务
|
|
48
|
+
await cls.shutdown_consumers(timeout)
|
|
49
|
+
|
|
50
|
+
# 3. 关闭所有客户端
|
|
51
|
+
await cls.shutdown_clients(timeout)
|
|
52
|
+
|
|
53
|
+
# 4. 关闭连接池
|
|
54
|
+
await cls.shutdown_core_resources(timeout)
|
|
55
|
+
|
|
56
|
+
# 5. 清理剩余状态
|
|
57
|
+
cls.clear_senders()
|
|
58
|
+
|
|
59
|
+
logger.info("RabbitMQService已完全关闭")
|
|
@@ -0,0 +1,212 @@
|
|
|
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, queue_name: str, **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
|
+
# 纯发送器场景,强制不创建队列
|
|
86
|
+
is_sender_only = not cls._has_listeners and cls._has_senders
|
|
87
|
+
|
|
88
|
+
# 发送器场景下强制禁用队列创建
|
|
89
|
+
create_if_not_exists = False if is_sender_only else cls._has_listeners
|
|
90
|
+
|
|
91
|
+
processed_queue_name = queue_name
|
|
92
|
+
# 只有非纯发送器场景才处理队列名称拼接
|
|
93
|
+
if not is_sender_only and create_if_not_exists and processed_queue_name and app_name:
|
|
94
|
+
if not processed_queue_name.endswith(f".{app_name}"):
|
|
95
|
+
processed_queue_name = f"{processed_queue_name}.{app_name}"
|
|
96
|
+
logger.info(f"监听器队列名称自动拼接app-name: {processed_queue_name}")
|
|
97
|
+
else:
|
|
98
|
+
logger.info(f"监听器队列已包含app-name: {processed_queue_name}")
|
|
99
|
+
|
|
100
|
+
logger.info(
|
|
101
|
+
f"创建客户端 - 队列: {processed_queue_name if not is_sender_only else 'N/A'}, "
|
|
102
|
+
f"纯发送器模式: {is_sender_only}, "
|
|
103
|
+
f"允许创建队列: {create_if_not_exists}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# 创建客户端实例
|
|
107
|
+
client = RabbitMQClient(
|
|
108
|
+
connection_pool=cls._connection_pool,
|
|
109
|
+
exchange_name=cls._config.get(
|
|
110
|
+
'exchange_name', "system.topic.exchange"),
|
|
111
|
+
exchange_type=kwargs.get('exchange_type', "topic"),
|
|
112
|
+
# 纯发送器场景下队列名称设为None
|
|
113
|
+
queue_name=None if is_sender_only else processed_queue_name,
|
|
114
|
+
routing_key=kwargs.get(
|
|
115
|
+
'routing_key',
|
|
116
|
+
f"{queue_name.split('.')[0]}.#" if queue_name and not is_sender_only else "#"
|
|
117
|
+
),
|
|
118
|
+
durable=kwargs.get('durable', True),
|
|
119
|
+
auto_delete=kwargs.get('auto_delete', False),
|
|
120
|
+
auto_parse_json=kwargs.get('auto_parse_json', True),
|
|
121
|
+
create_if_not_exists=create_if_not_exists,
|
|
122
|
+
prefetch_count=kwargs.get('prefetch_count', 2),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# 连接客户端
|
|
126
|
+
await client.connect()
|
|
127
|
+
|
|
128
|
+
return client
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
async def get_client(
|
|
132
|
+
cls,
|
|
133
|
+
client_name: str = "default", **kwargs
|
|
134
|
+
) -> RabbitMQClient:
|
|
135
|
+
"""获取或创建RabbitMQ客户端"""
|
|
136
|
+
if cls._is_shutdown:
|
|
137
|
+
raise RuntimeError("RabbitMQService已关闭,无法获取客户端")
|
|
138
|
+
|
|
139
|
+
# 等待连接池就绪
|
|
140
|
+
await cls.wait_for_pool_ready()
|
|
141
|
+
|
|
142
|
+
# 确保锁存在
|
|
143
|
+
if client_name not in cls._init_locks:
|
|
144
|
+
cls._init_locks[client_name] = asyncio.Lock()
|
|
145
|
+
|
|
146
|
+
async with cls._init_locks[client_name]:
|
|
147
|
+
# 如果客户端已存在且连接有效,直接返回
|
|
148
|
+
if client_name in cls._clients:
|
|
149
|
+
client = cls._clients[client_name]
|
|
150
|
+
is_sender_only = not cls._has_listeners and cls._has_senders
|
|
151
|
+
|
|
152
|
+
if await client.is_connected:
|
|
153
|
+
# 只有非纯发送器场景才检查队列初始化状态
|
|
154
|
+
if not is_sender_only and not client._queue and cls._has_listeners:
|
|
155
|
+
logger.info(f"客户端 '{client_name}' 队列未初始化,重新连接")
|
|
156
|
+
client.create_if_not_exists = True
|
|
157
|
+
await client.connect()
|
|
158
|
+
return client
|
|
159
|
+
else:
|
|
160
|
+
logger.info(f"客户端 '{client_name}' 连接已断开,重新连接")
|
|
161
|
+
if not is_sender_only:
|
|
162
|
+
client.create_if_not_exists = cls._has_listeners
|
|
163
|
+
await client.connect()
|
|
164
|
+
return client
|
|
165
|
+
|
|
166
|
+
# 创建新客户端
|
|
167
|
+
initial_queue_name = kwargs.pop('queue_name', '')
|
|
168
|
+
is_sender_only = not cls._has_listeners and cls._has_senders
|
|
169
|
+
|
|
170
|
+
# 发送器特殊处理
|
|
171
|
+
if is_sender_only:
|
|
172
|
+
kwargs['create_if_not_exists'] = False
|
|
173
|
+
client = await cls._create_client(
|
|
174
|
+
"", # 空队列名
|
|
175
|
+
app_name=cls._config.get("APP_NAME", ""),
|
|
176
|
+
**kwargs
|
|
177
|
+
)
|
|
178
|
+
cls._clients[client_name] = client
|
|
179
|
+
return client
|
|
180
|
+
|
|
181
|
+
# 监听器逻辑
|
|
182
|
+
kwargs['create_if_not_exists'] = True
|
|
183
|
+
client = await cls._create_client(
|
|
184
|
+
initial_queue_name,
|
|
185
|
+
app_name=cls._config.get("APP_NAME", ""),
|
|
186
|
+
**kwargs
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# 验证队列创建
|
|
190
|
+
if not is_sender_only and not client._queue:
|
|
191
|
+
logger.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
|
|
192
|
+
client.create_if_not_exists = True
|
|
193
|
+
await client.connect()
|
|
194
|
+
if not client._queue:
|
|
195
|
+
raise Exception(f"无法创建队列 '{initial_queue_name}'")
|
|
196
|
+
|
|
197
|
+
cls._clients[client_name] = client
|
|
198
|
+
return client
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
async def shutdown_clients(cls, timeout: float = 15.0) -> None:
|
|
202
|
+
"""关闭所有客户端"""
|
|
203
|
+
# 关闭所有客户端
|
|
204
|
+
for client in cls._clients.values():
|
|
205
|
+
try:
|
|
206
|
+
await client.close()
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.error(f"关闭客户端失败: {str(e)}")
|
|
209
|
+
|
|
210
|
+
# 清理客户端状态
|
|
211
|
+
cls._clients.clear()
|
|
212
|
+
cls._init_locks.clear()
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|