sycommon-python-lib 0.1.8__tar.gz → 0.1.10__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.
Potentially problematic release.
This version of sycommon-python-lib might be problematic. Click here for more details.
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/PKG-INFO +1 -1
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/pyproject.toml +1 -1
- sycommon_python_lib-0.1.10/src/sycommon/middleware/docs.py +30 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/middleware.py +4 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/traceid.py +32 -4
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/base_http.py +27 -25
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/rabbitmq/rabbitmq_client.py +68 -40
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/rabbitmq/rabbitmq_service.py +103 -46
- sycommon_python_lib-0.1.10/src/sycommon/services.py +212 -0
- sycommon_python_lib-0.1.10/src/sycommon/tools/docs.py +42 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon_python_lib.egg-info/SOURCES.txt +2 -0
- sycommon_python_lib-0.1.8/src/sycommon/services.py +0 -217
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/README.md +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/setup.cfg +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from fastapi import FastAPI, APIRouter
|
|
2
|
+
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def setup_docs_handler(app: FastAPI):
|
|
6
|
+
docs_router = APIRouter()
|
|
7
|
+
|
|
8
|
+
@docs_router.get("/docs", include_in_schema=False)
|
|
9
|
+
async def custom_swagger_ui_html():
|
|
10
|
+
return get_swagger_ui_html(
|
|
11
|
+
openapi_url=app.openapi_url,
|
|
12
|
+
title=f"{app.title}",
|
|
13
|
+
swagger_favicon_url="https://static.sytechnology.com/img/sylogopng.png",
|
|
14
|
+
swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui-bundle.js",
|
|
15
|
+
swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui.css",
|
|
16
|
+
swagger_ui_parameters={"defaultModelsExpandDepth": -1},
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
@docs_router.get("/redoc", include_in_schema=False)
|
|
20
|
+
async def custom_redoc_html():
|
|
21
|
+
return get_redoc_html(
|
|
22
|
+
openapi_url=app.openapi_url,
|
|
23
|
+
title=f"{app.title}",
|
|
24
|
+
redoc_favicon_url="https://static.sytechnology.com/img/sylogopng.png",
|
|
25
|
+
redoc_js_url="https://cdn.bootcdn.net/ajax/libs/redoc/2.1.5/redoc.standalone.js",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
app.include_router(docs_router)
|
|
29
|
+
|
|
30
|
+
return app
|
{sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/middleware/middleware.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from sycommon.health.ping import setup_ping_handler
|
|
2
2
|
from sycommon.middleware.cors import setup_cors_handler
|
|
3
|
+
from sycommon.middleware.docs import setup_docs_handler
|
|
3
4
|
from sycommon.middleware.exception import setup_exception_handler
|
|
4
5
|
from sycommon.middleware.monitor_memory import setup_monitor_memory_middleware
|
|
5
6
|
from sycommon.middleware.mq import setup_mq_middleware
|
|
@@ -36,4 +37,7 @@ class Middleware:
|
|
|
36
37
|
# 添加mq中间件
|
|
37
38
|
# app = setup_mq_middleware(app)
|
|
38
39
|
|
|
40
|
+
# doc
|
|
41
|
+
# app = setup_docs_handler(app)
|
|
42
|
+
|
|
39
43
|
return app
|
|
@@ -78,13 +78,19 @@ def setup_trace_id_handler(app):
|
|
|
78
78
|
response = await call_next(request)
|
|
79
79
|
|
|
80
80
|
content_type = response.headers.get("Content-Type", "")
|
|
81
|
+
|
|
82
|
+
# 处理 SSE 响应 - 关键修复点
|
|
81
83
|
if "text/event-stream" in content_type:
|
|
82
|
-
#
|
|
84
|
+
# 流式响应不能有Content-Length,移除它
|
|
85
|
+
if "Content-Length" in response.headers:
|
|
86
|
+
del response.headers["Content-Length"]
|
|
83
87
|
response.headers["x-traceId-header"] = trace_id
|
|
84
88
|
return response
|
|
85
89
|
|
|
90
|
+
# 处理普通响应
|
|
86
91
|
response_body = b""
|
|
87
92
|
try:
|
|
93
|
+
# 收集所有响应块
|
|
88
94
|
async for chunk in response.body_iterator:
|
|
89
95
|
response_body += chunk
|
|
90
96
|
|
|
@@ -98,14 +104,36 @@ def setup_trace_id_handler(app):
|
|
|
98
104
|
data["traceId"] = trace_id
|
|
99
105
|
new_body = json.dumps(
|
|
100
106
|
data, ensure_ascii=False).encode()
|
|
107
|
+
|
|
108
|
+
# 创建新响应,确保Content-Length正确
|
|
101
109
|
response = Response(
|
|
102
110
|
content=new_body,
|
|
103
111
|
status_code=response.status_code,
|
|
104
|
-
headers=dict(response.headers)
|
|
112
|
+
headers=dict(response.headers),
|
|
113
|
+
media_type=response.media_type
|
|
105
114
|
)
|
|
115
|
+
# 显式设置正确的Content-Length
|
|
106
116
|
response.headers["Content-Length"] = str(len(new_body))
|
|
107
117
|
except json.JSONDecodeError:
|
|
108
|
-
|
|
118
|
+
# 如果不是JSON,恢复原始响应体并更新长度
|
|
119
|
+
response = Response(
|
|
120
|
+
content=response_body,
|
|
121
|
+
status_code=response.status_code,
|
|
122
|
+
headers=dict(response.headers),
|
|
123
|
+
media_type=response.media_type
|
|
124
|
+
)
|
|
125
|
+
response.headers["Content-Length"] = str(
|
|
126
|
+
len(response_body))
|
|
127
|
+
else:
|
|
128
|
+
# 非JSON响应,恢复原始响应体
|
|
129
|
+
response = Response(
|
|
130
|
+
content=response_body,
|
|
131
|
+
status_code=response.status_code,
|
|
132
|
+
headers=dict(response.headers),
|
|
133
|
+
media_type=response.media_type
|
|
134
|
+
)
|
|
135
|
+
response.headers["Content-Length"] = str(
|
|
136
|
+
len(response_body))
|
|
109
137
|
except StopAsyncIteration:
|
|
110
138
|
pass
|
|
111
139
|
|
|
@@ -129,7 +157,7 @@ def setup_trace_id_handler(app):
|
|
|
129
157
|
"uploaded_files": files_info if files_info else None
|
|
130
158
|
}
|
|
131
159
|
error_message_str = json.dumps(error_message, ensure_ascii=False)
|
|
132
|
-
SYLogger.error(error_message_str)
|
|
160
|
+
SYLogger.error(error_message_str)
|
|
133
161
|
raise
|
|
134
162
|
finally:
|
|
135
163
|
# 清理上下文变量,防止泄漏
|
|
@@ -3,14 +3,15 @@ from pydantic import BaseModel, Field
|
|
|
3
3
|
from fastapi.responses import JSONResponse
|
|
4
4
|
from fastapi import status
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# 支持任意类型的泛型约束
|
|
7
7
|
T = TypeVar('T')
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class BaseResponseModel(BaseModel, Generic[T]):
|
|
11
|
-
"""
|
|
12
|
-
code: int = Field(default=0, description="
|
|
13
|
-
message: str = Field(default=
|
|
11
|
+
"""基础响应模型,支持自定义状态码"""
|
|
12
|
+
code: int = Field(default=0, description="业务响应码,默认0成功,1失败,支持自定义")
|
|
13
|
+
message: str | None = Field(default=None, description="业务响应信息")
|
|
14
|
+
success: bool = Field(default=True, description="请求是否成功")
|
|
14
15
|
data: T | None = Field(default=None, description="业务响应数据,支持任意类型")
|
|
15
16
|
traceId: str | None = Field(default=None, description="请求链路追踪ID")
|
|
16
17
|
|
|
@@ -22,22 +23,22 @@ class BaseResponseModel(BaseModel, Generic[T]):
|
|
|
22
23
|
def build_response_content(
|
|
23
24
|
data: T | Any = None,
|
|
24
25
|
code: int = 0,
|
|
25
|
-
message: str =
|
|
26
|
+
message: str = None
|
|
26
27
|
) -> dict:
|
|
27
28
|
"""
|
|
28
|
-
|
|
29
|
+
构建响应内容字典,自动根据code判断success
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
message: 响应信息
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
响应内容字典,格式为{"code": int, "message": str, "data": Any}
|
|
31
|
+
规则:
|
|
32
|
+
- code为0时success=True(默认成功)
|
|
33
|
+
- 其他任何code值success=False(包括200等自定义状态码)
|
|
37
34
|
"""
|
|
35
|
+
# 成功状态仅当code为0时成立,其他任何code都视为失败
|
|
36
|
+
success = code == 0 or code == 200
|
|
37
|
+
|
|
38
38
|
response = BaseResponseModel(
|
|
39
39
|
code=code,
|
|
40
40
|
message=message,
|
|
41
|
+
success=success,
|
|
41
42
|
data=data
|
|
42
43
|
)
|
|
43
44
|
|
|
@@ -45,6 +46,7 @@ def build_response_content(
|
|
|
45
46
|
return {
|
|
46
47
|
"code": response.code,
|
|
47
48
|
"message": response.message,
|
|
49
|
+
"success": response.success,
|
|
48
50
|
"data": response.data.model_dump()
|
|
49
51
|
}
|
|
50
52
|
else:
|
|
@@ -54,10 +56,10 @@ def build_response_content(
|
|
|
54
56
|
def create_response(
|
|
55
57
|
data: T | Any = None,
|
|
56
58
|
code: int = 0,
|
|
57
|
-
message: str =
|
|
59
|
+
message: str = None,
|
|
58
60
|
status_code: int = status.HTTP_200_OK
|
|
59
61
|
) -> JSONResponse:
|
|
60
|
-
"""
|
|
62
|
+
"""创建完整响应,支持自定义业务状态码"""
|
|
61
63
|
content = build_response_content(data=data, code=code, message=message)
|
|
62
64
|
return JSONResponse(
|
|
63
65
|
content=content,
|
|
@@ -65,18 +67,18 @@ def create_response(
|
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
|
|
68
|
-
def success_response(data: T | Any = None,
|
|
69
|
-
"""
|
|
70
|
-
return create_response(data=data,
|
|
70
|
+
def success_response(data: T | Any = None, code: int = 0) -> JSONResponse:
|
|
71
|
+
"""快捷创建成功响应(code=0, success=True)"""
|
|
72
|
+
return create_response(data=data, code=code)
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
def error_response(
|
|
74
|
-
message: str =
|
|
76
|
+
message: str = None,
|
|
75
77
|
code: int = 1,
|
|
76
78
|
data: T | Any = None,
|
|
77
79
|
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
78
80
|
) -> JSONResponse:
|
|
79
|
-
"""
|
|
81
|
+
"""快捷创建错误响应,支持自定义错误码(如200)"""
|
|
80
82
|
return create_response(
|
|
81
83
|
data=data,
|
|
82
84
|
code=code,
|
|
@@ -85,15 +87,15 @@ def error_response(
|
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
|
|
88
|
-
def success_content(data: T | Any = None
|
|
89
|
-
"""
|
|
90
|
-
return build_response_content(data=data
|
|
90
|
+
def success_content(data: T | Any = None) -> dict:
|
|
91
|
+
"""构建成功响应内容字典"""
|
|
92
|
+
return build_response_content(data=data)
|
|
91
93
|
|
|
92
94
|
|
|
93
95
|
def error_content(
|
|
94
|
-
message: str =
|
|
96
|
+
message: str = None,
|
|
95
97
|
code: int = 1,
|
|
96
98
|
data: T | Any = None
|
|
97
99
|
) -> dict:
|
|
98
|
-
"""
|
|
100
|
+
"""构建错误响应内容字典,支持自定义错误码"""
|
|
99
101
|
return build_response_content(data=data, code=code, message=message)
|
{sycommon_python_lib-0.1.8 → sycommon_python_lib-0.1.10}/src/sycommon/rabbitmq/rabbitmq_client.py
RENAMED
|
@@ -183,8 +183,14 @@ class RabbitMQClient:
|
|
|
183
183
|
await asyncio.sleep(1)
|
|
184
184
|
return False
|
|
185
185
|
|
|
186
|
-
async def connect(self, force_reconnect: bool = False,
|
|
187
|
-
"""
|
|
186
|
+
async def connect(self, force_reconnect: bool = False, declare_queue: bool = True) -> None:
|
|
187
|
+
"""建立连接并检查/创建资源,新增declare_queue参数控制是否声明队列"""
|
|
188
|
+
# 增加日志确认参数状态
|
|
189
|
+
logging.debug(
|
|
190
|
+
f"connect() 调用 - force_reconnect={force_reconnect}, "
|
|
191
|
+
f"declare_queue={declare_queue}, create_if_not_exists={self.create_if_not_exists}"
|
|
192
|
+
)
|
|
193
|
+
|
|
188
194
|
if self.is_connected and not force_reconnect:
|
|
189
195
|
return
|
|
190
196
|
|
|
@@ -196,7 +202,8 @@ class RabbitMQClient:
|
|
|
196
202
|
f"尝试连接RabbitMQ - 主机: {self.host}:{self.port}, "
|
|
197
203
|
f"虚拟主机: {self.virtualhost}, "
|
|
198
204
|
f"队列: {self.queue_name}, "
|
|
199
|
-
f"
|
|
205
|
+
f"声明队列: {declare_queue}, "
|
|
206
|
+
f"允许创建: {self.create_if_not_exists}"
|
|
200
207
|
)
|
|
201
208
|
|
|
202
209
|
# 重置状态
|
|
@@ -207,13 +214,13 @@ class RabbitMQClient:
|
|
|
207
214
|
retries = 0
|
|
208
215
|
last_exception = None
|
|
209
216
|
|
|
210
|
-
while retries <
|
|
217
|
+
while retries < 3: # 使用固定重试次数
|
|
211
218
|
try:
|
|
212
219
|
# 关闭旧连接
|
|
213
220
|
if self.connection and not self.connection.is_closed:
|
|
214
221
|
await self.connection.close()
|
|
215
222
|
|
|
216
|
-
# 建立新连接
|
|
223
|
+
# 建立新连接
|
|
217
224
|
self.connection = await asyncio.wait_for(
|
|
218
225
|
aio_pika.connect_robust(
|
|
219
226
|
host=self.host,
|
|
@@ -223,7 +230,7 @@ class RabbitMQClient:
|
|
|
223
230
|
virtualhost=self.virtualhost,
|
|
224
231
|
heartbeat=self.heartbeat,
|
|
225
232
|
client_properties={
|
|
226
|
-
"connection_name": self.app_name or "rabbitmq-client"}
|
|
233
|
+
"connection_name": self.app_name or "rabbitmq-client"}
|
|
227
234
|
),
|
|
228
235
|
timeout=self.connection_timeout
|
|
229
236
|
)
|
|
@@ -262,28 +269,29 @@ class RabbitMQClient:
|
|
|
262
269
|
)
|
|
263
270
|
logging.info(f"使用已存在的交换机 '{self.exchange_name}'")
|
|
264
271
|
|
|
265
|
-
# 2. 处理队列
|
|
266
|
-
if self.queue_name:
|
|
272
|
+
# 2. 处理队列 - 只有declare_queue为True时才处理
|
|
273
|
+
if declare_queue and self.queue_name:
|
|
267
274
|
queue_exists = await self._check_queue_exists()
|
|
268
275
|
|
|
269
276
|
if not queue_exists:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
self.queue = await asyncio.wait_for(
|
|
273
|
-
self.channel.declare_queue(
|
|
274
|
-
name=self.queue_name,
|
|
275
|
-
durable=self.durable,
|
|
276
|
-
auto_delete=self.auto_delete,
|
|
277
|
-
exclusive=False,
|
|
278
|
-
passive=False
|
|
279
|
-
),
|
|
280
|
-
timeout=self.rpc_timeout
|
|
281
|
-
)
|
|
282
|
-
self._queue_exists = True
|
|
283
|
-
logging.info(f"已创建队列 '{self.queue_name}'")
|
|
284
|
-
else:
|
|
277
|
+
# 关键检查点:确保有权限创建队列
|
|
278
|
+
if not self.create_if_not_exists:
|
|
285
279
|
raise Exception(
|
|
286
280
|
f"队列 '{self.queue_name}' 不存在且不允许自动创建")
|
|
281
|
+
|
|
282
|
+
# 创建队列
|
|
283
|
+
self.queue = await asyncio.wait_for(
|
|
284
|
+
self.channel.declare_queue(
|
|
285
|
+
name=self.queue_name,
|
|
286
|
+
durable=self.durable,
|
|
287
|
+
auto_delete=self.auto_delete,
|
|
288
|
+
exclusive=False,
|
|
289
|
+
passive=False
|
|
290
|
+
),
|
|
291
|
+
timeout=self.rpc_timeout
|
|
292
|
+
)
|
|
293
|
+
self._queue_exists = True
|
|
294
|
+
logging.info(f"已创建队列 '{self.queue_name}'")
|
|
287
295
|
else:
|
|
288
296
|
# 获取已有队列
|
|
289
297
|
self.queue = await asyncio.wait_for(
|
|
@@ -292,12 +300,18 @@ class RabbitMQClient:
|
|
|
292
300
|
)
|
|
293
301
|
logging.info(f"使用已存在的队列 '{self.queue_name}'")
|
|
294
302
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
# 3. 绑定队列到交换机
|
|
304
|
+
if self.queue and self.exchange:
|
|
305
|
+
bound = await self._bind_queue()
|
|
306
|
+
if not bound:
|
|
307
|
+
raise Exception(
|
|
308
|
+
f"队列 '{self.queue_name}' 绑定到交换机 '{self.exchange_name}' 失败")
|
|
309
|
+
else:
|
|
310
|
+
# 不声明队列时,将队列相关状态设为False
|
|
311
|
+
self.queue = None
|
|
312
|
+
self._queue_exists = False
|
|
313
|
+
self._queue_bound = False
|
|
314
|
+
logging.debug(f"跳过队列 '{self.queue_name}' 的声明和绑定")
|
|
301
315
|
|
|
302
316
|
# 如果之前在消费,重新开始消费
|
|
303
317
|
if self._is_consuming and self.message_handler:
|
|
@@ -308,23 +322,22 @@ class RabbitMQClient:
|
|
|
308
322
|
self._start_keepalive_task()
|
|
309
323
|
|
|
310
324
|
self._update_activity_timestamp()
|
|
311
|
-
logging.info(
|
|
325
|
+
logging.info(
|
|
326
|
+
f"RabbitMQ客户端连接成功 (队列: {self.queue_name}, 声明队列: {declare_queue})")
|
|
312
327
|
return
|
|
313
328
|
|
|
314
329
|
except Exception as e:
|
|
315
330
|
retries += 1
|
|
316
331
|
last_exception = e
|
|
317
332
|
logging.warning(
|
|
318
|
-
f"连接失败({retries}/
|
|
333
|
+
f"连接失败({retries}/3): {str(e)}, create_if_not_exists={self.create_if_not_exists}, 重试中...")
|
|
319
334
|
|
|
320
|
-
if retries <
|
|
335
|
+
if retries < 3:
|
|
321
336
|
await asyncio.sleep(self.reconnection_delay)
|
|
322
337
|
|
|
323
|
-
logging.error(f"最终连接失败: {str(last_exception)}"
|
|
338
|
+
logging.error(f"最终连接失败: {str(last_exception)}")
|
|
324
339
|
raise Exception(
|
|
325
|
-
f"经过{
|
|
326
|
-
f"最后错误: {str(last_exception)}"
|
|
327
|
-
)
|
|
340
|
+
f"经过3次重试后仍无法完成连接和资源初始化。最后错误: {str(last_exception)}")
|
|
328
341
|
|
|
329
342
|
def _start_connection_monitor(self):
|
|
330
343
|
"""启动连接监控任务,检测连接/通道关闭"""
|
|
@@ -534,16 +547,31 @@ class RabbitMQClient:
|
|
|
534
547
|
self.message_handler = handler
|
|
535
548
|
|
|
536
549
|
async def start_consuming(self, timeout: Optional[float] = None) -> str:
|
|
537
|
-
"""开始消费消息并返回consumer_tag
|
|
550
|
+
"""开始消费消息并返回consumer_tag,支持超时控制和队列检查重试"""
|
|
538
551
|
if self._is_consuming:
|
|
539
552
|
logging.debug("已经在消费中,返回现有consumer_tag")
|
|
540
553
|
return self._consumer_tag
|
|
541
554
|
|
|
542
|
-
|
|
543
|
-
|
|
555
|
+
# 增加队列检查和连接确保逻辑
|
|
556
|
+
max_attempts = 5
|
|
557
|
+
attempt = 0
|
|
558
|
+
while attempt < max_attempts:
|
|
559
|
+
if not self.is_connected:
|
|
560
|
+
await self.connect()
|
|
561
|
+
|
|
562
|
+
if self.queue:
|
|
563
|
+
break
|
|
564
|
+
|
|
565
|
+
attempt += 1
|
|
566
|
+
logging.warning(f"队列尚未初始化,等待后重试({attempt}/{max_attempts})")
|
|
567
|
+
await asyncio.sleep(1)
|
|
544
568
|
|
|
545
569
|
if not self.queue:
|
|
546
|
-
|
|
570
|
+
# 最后尝试一次显式连接并声明队列
|
|
571
|
+
logging.warning("最后尝试重新连接并声明队列")
|
|
572
|
+
await self.connect(force_reconnect=True, declare_queue=True)
|
|
573
|
+
if not self.queue:
|
|
574
|
+
raise Exception("队列未初始化,多次尝试后仍无法创建")
|
|
547
575
|
|
|
548
576
|
if not self.message_handler:
|
|
549
577
|
raise Exception("未设置消息处理函数")
|