jettask 0.2.23__py3-none-any.whl → 0.2.24__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.
- jettask/__init__.py +2 -0
- jettask/cli.py +12 -8
- jettask/config/lua_scripts.py +37 -0
- jettask/config/nacos_config.py +1 -1
- jettask/core/app.py +313 -340
- jettask/core/container.py +4 -4
- jettask/{persistence → core}/namespace.py +93 -27
- jettask/core/task.py +16 -9
- jettask/core/unified_manager_base.py +136 -26
- jettask/db/__init__.py +67 -0
- jettask/db/base.py +137 -0
- jettask/{utils/db_connector.py → db/connector.py} +130 -26
- jettask/db/models/__init__.py +16 -0
- jettask/db/models/scheduled_task.py +196 -0
- jettask/db/models/task.py +77 -0
- jettask/db/models/task_run.py +85 -0
- jettask/executor/__init__.py +0 -15
- jettask/executor/core.py +76 -31
- jettask/executor/process_entry.py +29 -114
- jettask/executor/task_executor.py +4 -0
- jettask/messaging/event_pool.py +928 -685
- jettask/messaging/scanner.py +30 -0
- jettask/persistence/__init__.py +28 -103
- jettask/persistence/buffer.py +170 -0
- jettask/persistence/consumer.py +330 -249
- jettask/persistence/manager.py +304 -0
- jettask/persistence/persistence.py +391 -0
- jettask/scheduler/__init__.py +15 -3
- jettask/scheduler/{task_crud.py → database.py} +61 -57
- jettask/scheduler/loader.py +2 -2
- jettask/scheduler/{scheduler_coordinator.py → manager.py} +23 -6
- jettask/scheduler/models.py +14 -10
- jettask/scheduler/schedule.py +166 -0
- jettask/scheduler/scheduler.py +12 -11
- jettask/schemas/__init__.py +50 -1
- jettask/schemas/backlog.py +43 -6
- jettask/schemas/namespace.py +70 -19
- jettask/schemas/queue.py +19 -3
- jettask/schemas/responses.py +493 -0
- jettask/task/__init__.py +0 -2
- jettask/task/router.py +3 -0
- jettask/test_connection_monitor.py +1 -1
- jettask/utils/__init__.py +7 -5
- jettask/utils/db_init.py +8 -4
- jettask/utils/namespace_dep.py +167 -0
- jettask/utils/queue_matcher.py +186 -0
- jettask/utils/rate_limit/concurrency_limiter.py +7 -1
- jettask/utils/stream_backlog.py +1 -1
- jettask/webui/__init__.py +0 -1
- jettask/webui/api/__init__.py +4 -4
- jettask/webui/api/alerts.py +806 -71
- jettask/webui/api/example_refactored.py +400 -0
- jettask/webui/api/namespaces.py +390 -45
- jettask/webui/api/overview.py +300 -54
- jettask/webui/api/queues.py +971 -267
- jettask/webui/api/scheduled.py +1249 -56
- jettask/webui/api/settings.py +129 -7
- jettask/webui/api/workers.py +442 -0
- jettask/webui/app.py +46 -2329
- jettask/webui/middleware/__init__.py +6 -0
- jettask/webui/middleware/namespace_middleware.py +135 -0
- jettask/webui/services/__init__.py +146 -0
- jettask/webui/services/heartbeat_service.py +251 -0
- jettask/webui/services/overview_service.py +60 -51
- jettask/webui/services/queue_monitor_service.py +426 -0
- jettask/webui/services/redis_monitor_service.py +87 -0
- jettask/webui/services/settings_service.py +174 -111
- jettask/webui/services/task_monitor_service.py +222 -0
- jettask/webui/services/timeline_pg_service.py +452 -0
- jettask/webui/services/timeline_service.py +189 -0
- jettask/webui/services/worker_monitor_service.py +467 -0
- jettask/webui/utils/__init__.py +11 -0
- jettask/webui/utils/time_utils.py +122 -0
- jettask/worker/lifecycle.py +8 -2
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/METADATA +1 -1
- jettask-0.2.24.dist-info/RECORD +142 -0
- jettask/executor/executor.py +0 -338
- jettask/persistence/backlog_monitor.py +0 -567
- jettask/persistence/base.py +0 -2334
- jettask/persistence/db_manager.py +0 -516
- jettask/persistence/maintenance.py +0 -81
- jettask/persistence/message_consumer.py +0 -259
- jettask/persistence/models.py +0 -49
- jettask/persistence/offline_recovery.py +0 -196
- jettask/persistence/queue_discovery.py +0 -215
- jettask/persistence/task_persistence.py +0 -218
- jettask/persistence/task_updater.py +0 -583
- jettask/scheduler/add_execution_count.sql +0 -11
- jettask/scheduler/add_priority_field.sql +0 -26
- jettask/scheduler/add_scheduler_id.sql +0 -25
- jettask/scheduler/add_scheduler_id_index.sql +0 -10
- jettask/scheduler/make_scheduler_id_required.sql +0 -28
- jettask/scheduler/migrate_interval_seconds.sql +0 -9
- jettask/scheduler/performance_optimization.sql +0 -45
- jettask/scheduler/run_scheduler.py +0 -186
- jettask/scheduler/schema.sql +0 -84
- jettask/task/task_executor.py +0 -318
- jettask/webui/api/analytics.py +0 -323
- jettask/webui/config.py +0 -90
- jettask/webui/models/__init__.py +0 -3
- jettask/webui/models/namespace.py +0 -63
- jettask/webui/namespace_manager/__init__.py +0 -10
- jettask/webui/namespace_manager/multi.py +0 -593
- jettask/webui/namespace_manager/unified.py +0 -193
- jettask/webui/run.py +0 -46
- jettask-0.2.23.dist-info/RECORD +0 -145
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/WHEEL +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/top_level.txt +0 -0
jettask/core/container.py
CHANGED
@@ -74,7 +74,7 @@ class ServiceContainer:
|
|
74
74
|
|
75
75
|
def _get_sync_text_client(self) -> redis.Redis:
|
76
76
|
"""获取同步文本模式客户端(使用全局客户端实例)"""
|
77
|
-
from jettask.
|
77
|
+
from jettask.db.connector import get_sync_redis_client
|
78
78
|
return get_sync_redis_client(
|
79
79
|
redis_url=self.config.redis.url,
|
80
80
|
decode_responses=True,
|
@@ -83,7 +83,7 @@ class ServiceContainer:
|
|
83
83
|
|
84
84
|
def _get_sync_binary_client(self) -> redis.Redis:
|
85
85
|
"""获取同步二进制模式客户端(使用全局客户端实例)"""
|
86
|
-
from jettask.
|
86
|
+
from jettask.db.connector import get_sync_redis_client
|
87
87
|
return get_sync_redis_client(
|
88
88
|
redis_url=self.config.redis.url,
|
89
89
|
decode_responses=False,
|
@@ -92,7 +92,7 @@ class ServiceContainer:
|
|
92
92
|
|
93
93
|
def _get_async_text_client(self) -> aioredis.Redis:
|
94
94
|
"""获取异步文本模式客户端(使用全局客户端实例)"""
|
95
|
-
from jettask.
|
95
|
+
from jettask.db.connector import get_async_redis_client
|
96
96
|
return get_async_redis_client(
|
97
97
|
redis_url=self.config.redis.url,
|
98
98
|
decode_responses=True,
|
@@ -101,7 +101,7 @@ class ServiceContainer:
|
|
101
101
|
|
102
102
|
def _get_async_binary_client(self) -> aioredis.Redis:
|
103
103
|
"""获取异步二进制模式客户端(使用全局客户端实例)"""
|
104
|
-
from jettask.
|
104
|
+
from jettask.db.connector import get_async_redis_client
|
105
105
|
return get_async_redis_client(
|
106
106
|
redis_url=self.config.redis.url,
|
107
107
|
decode_responses=False,
|
@@ -2,20 +2,16 @@
|
|
2
2
|
命名空间数据访问层 - 支持多租户的数据隔离访问
|
3
3
|
"""
|
4
4
|
import os
|
5
|
-
import asyncio
|
6
|
-
import json
|
7
5
|
import logging
|
8
|
-
import time
|
9
6
|
import traceback
|
10
|
-
from
|
11
|
-
from typing import Dict, List, Optional, Tuple, Any
|
7
|
+
from typing import Dict, List, Optional
|
12
8
|
import redis.asyncio as redis
|
13
|
-
from sqlalchemy import text
|
9
|
+
from sqlalchemy import text
|
14
10
|
from sqlalchemy.ext.asyncio import AsyncSession
|
15
11
|
import aiohttp
|
16
12
|
|
17
13
|
# 导入统一的数据库连接工具
|
18
|
-
from
|
14
|
+
from jettask.db.connector import (
|
19
15
|
get_dual_mode_async_redis_client,
|
20
16
|
get_pg_engine_and_factory
|
21
17
|
)
|
@@ -49,13 +45,20 @@ class NamespaceConnection:
|
|
49
45
|
try:
|
50
46
|
# 初始化 PostgreSQL 连接(使用全局单例)
|
51
47
|
if self.pg_config:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
48
|
+
# pg_config 可以是字符串(DSN)或者字典(包含url字段)
|
49
|
+
pg_dsn = self.pg_config if isinstance(self.pg_config, str) else self.pg_config.get('url')
|
50
|
+
if pg_dsn:
|
51
|
+
# 将 postgresql:// 转换为 postgresql+asyncpg://
|
52
|
+
if pg_dsn.startswith('postgresql://'):
|
53
|
+
pg_dsn = pg_dsn.replace('postgresql://', 'postgresql+asyncpg://', 1)
|
54
|
+
|
55
|
+
self.async_engine, self.AsyncSessionLocal = get_pg_engine_and_factory(
|
56
|
+
dsn=pg_dsn,
|
57
|
+
pool_size=10,
|
58
|
+
max_overflow=5,
|
59
|
+
pool_recycle=3600,
|
60
|
+
echo=False
|
61
|
+
)
|
59
62
|
|
60
63
|
# 初始化 Redis 连接(使用全局单例,双模式)
|
61
64
|
if self.redis_config:
|
@@ -142,29 +145,92 @@ class NamespaceDataAccessManager:
|
|
142
145
|
return self._session
|
143
146
|
|
144
147
|
async def get_namespace_config(self, namespace_name: str) -> dict:
|
145
|
-
"""从任务中心API获取命名空间配置
|
148
|
+
"""从任务中心API获取命名空间配置
|
149
|
+
|
150
|
+
支持两种配置模式:
|
151
|
+
- nacos 模式:API 返回 nacos_key,本地通过 Nacos 获取真实配置
|
152
|
+
- direct 模式:API 直接返回完整的数据库 URL
|
153
|
+
"""
|
146
154
|
# 使用127.0.0.1替代localhost,确保容器内能正确连接
|
147
155
|
base_url = self.task_center_base_url
|
148
156
|
if 'localhost' in base_url:
|
149
157
|
base_url = base_url.replace('localhost', '127.0.0.1')
|
150
158
|
url = f"{base_url}/api/v1/namespaces/{namespace_name}"
|
151
|
-
|
159
|
+
|
152
160
|
try:
|
153
161
|
session = await self._get_session()
|
154
162
|
async with session.get(url) as resp:
|
155
163
|
if resp.status == 200:
|
156
164
|
data = await resp.json()
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
165
|
+
|
166
|
+
# 处理新格式(带 config_mode 字段)
|
167
|
+
redis_config_mode = data.get('redis_config_mode', 'direct')
|
168
|
+
pg_config_mode = data.get('pg_config_mode', 'direct')
|
169
|
+
|
170
|
+
# 处理 Redis 配置
|
171
|
+
redis_config = {}
|
172
|
+
if redis_config_mode == 'nacos':
|
173
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
174
|
+
redis_nacos_key = data.get('redis_nacos_key')
|
175
|
+
if redis_nacos_key:
|
176
|
+
logger.info(f"命名空间 {namespace_name} 使用 Nacos 模式获取 Redis 配置,key: {redis_nacos_key}")
|
177
|
+
try:
|
178
|
+
from jettask.config.nacos_config import config as nacos_config
|
179
|
+
redis_url = nacos_config.get(redis_nacos_key)
|
180
|
+
if redis_url:
|
181
|
+
redis_config = {'url': redis_url}
|
182
|
+
logger.debug(f"从 Nacos 获取 Redis URL 成功: {redis_nacos_key}")
|
183
|
+
else:
|
184
|
+
logger.warning(f"Nacos 配置键 {redis_nacos_key} 未找到或为空")
|
185
|
+
except Exception as e:
|
186
|
+
logger.error(f"从 Nacos 获取 Redis 配置失败: {e}")
|
187
|
+
raise ValueError(f"无法从 Nacos 获取 Redis 配置 (key: {redis_nacos_key}): {e}")
|
188
|
+
else:
|
189
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
190
|
+
redis_url = data.get('redis_url')
|
191
|
+
if redis_url:
|
192
|
+
redis_config = {'url': redis_url}
|
193
|
+
logger.debug(f"命名空间 {namespace_name} 使用 Direct 模式,Redis URL 已获取")
|
194
|
+
|
195
|
+
# 处理 PostgreSQL 配置
|
196
|
+
pg_config = {}
|
197
|
+
if pg_config_mode == 'nacos':
|
198
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
199
|
+
pg_nacos_key = data.get('pg_nacos_key')
|
200
|
+
if pg_nacos_key:
|
201
|
+
logger.info(f"命名空间 {namespace_name} 使用 Nacos 模式获取 PG 配置,key: {pg_nacos_key}")
|
202
|
+
try:
|
203
|
+
from jettask.config.nacos_config import config as nacos_config
|
204
|
+
pg_url = nacos_config.get(pg_nacos_key)
|
205
|
+
if pg_url:
|
206
|
+
pg_config = {'url': pg_url}
|
207
|
+
logger.debug(f"从 Nacos 获取 PG URL 成功: {pg_nacos_key}")
|
208
|
+
else:
|
209
|
+
logger.warning(f"Nacos 配置键 {pg_nacos_key} 未找到或为空")
|
210
|
+
except Exception as e:
|
211
|
+
logger.error(f"从 Nacos 获取 PG 配置失败: {e}")
|
212
|
+
# PostgreSQL 是可选的,不抛出异常
|
213
|
+
logger.warning(f"PostgreSQL 配置获取失败,将跳过 PG 相关功能")
|
214
|
+
else:
|
215
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
216
|
+
pg_url = data.get('pg_url')
|
217
|
+
if pg_url:
|
218
|
+
pg_config = {'url': pg_url}
|
219
|
+
logger.debug(f"命名空间 {namespace_name} 使用 Direct 模式,PG URL 已获取")
|
220
|
+
|
221
|
+
# 兼容旧格式:如果没有 config_mode 字段(旧版 API)
|
222
|
+
if not redis_config:
|
223
|
+
if data.get('redis_config'):
|
224
|
+
redis_config = data.get('redis_config')
|
225
|
+
elif data.get('redis_url'):
|
226
|
+
redis_config = {'url': data.get('redis_url')}
|
227
|
+
|
228
|
+
if not pg_config:
|
229
|
+
if data.get('pg_config'):
|
230
|
+
pg_config = data.get('pg_config')
|
231
|
+
elif data.get('pg_url'):
|
232
|
+
pg_config = {'url': data.get('pg_url')}
|
233
|
+
|
168
234
|
return {
|
169
235
|
'name': data.get('name'),
|
170
236
|
'redis_config': redis_config,
|
jettask/core/task.py
CHANGED
@@ -36,36 +36,41 @@ class Task:
|
|
36
36
|
trigger_time: float = None
|
37
37
|
retry_config: Optional[dict] = None # 存储任务级别的重试配置
|
38
38
|
|
39
|
-
def __call__(self, event_id: str, trigger_time: float, *args: Any, **kwds: Any) -> Any:
|
39
|
+
def __call__(self, event_id: str, trigger_time: float, queue:str, *args: Any, **kwds: Any) -> Any:
|
40
40
|
# 检查函数签名以进行依赖注入
|
41
41
|
injected_args, injected_kwargs = self._inject_dependencies(
|
42
|
-
event_id, trigger_time, args, kwds
|
42
|
+
event_id, trigger_time, queue, args, kwds
|
43
43
|
)
|
44
44
|
return self.run(*injected_args, **injected_kwargs)
|
45
45
|
|
46
|
-
def _inject_dependencies(self, event_id: str, trigger_time: float, args: tuple, kwargs: dict) -> tuple:
|
46
|
+
def _inject_dependencies(self, event_id: str, trigger_time: float, queue:str, args: tuple, kwargs: dict) -> tuple:
|
47
47
|
"""
|
48
48
|
基于类型注解自动注入TaskContext
|
49
49
|
"""
|
50
|
+
import logging
|
51
|
+
logger = logging.getLogger(__name__)
|
52
|
+
|
50
53
|
# 获取run方法的签名
|
51
54
|
try:
|
52
55
|
sig = inspect.signature(self.run)
|
53
56
|
type_hints = get_type_hints(self.run)
|
54
|
-
|
57
|
+
logger.debug(f"[TaskContext注入] 任务 {self.name} - 签名: {sig}, 类型提示: {type_hints}")
|
58
|
+
except (ValueError, TypeError, NameError) as e:
|
55
59
|
# 如果获取签名失败,返回原始参数
|
60
|
+
logger.warning(f"[TaskContext注入] 任务 {self.name} - 获取签名失败: {e}")
|
56
61
|
return args, kwargs
|
57
62
|
|
58
|
-
# 从kwargs中提取scheduled_task_id
|
59
|
-
#
|
63
|
+
# 从kwargs中提取scheduled_task_id和真实队列名(如果存在)
|
64
|
+
# 这些值由执行器从event_data中提取并传递
|
60
65
|
scheduled_task_id = kwargs.pop('__scheduled_task_id', None)
|
61
|
-
|
66
|
+
|
62
67
|
# 创建TaskContext实例
|
63
68
|
context = TaskContext(
|
64
69
|
event_id=event_id,
|
65
70
|
name=self.name,
|
66
71
|
trigger_time=trigger_time,
|
67
72
|
app=self._app,
|
68
|
-
queue=
|
73
|
+
queue=queue, # 优先使用真实队列名,fallback到任务定义的队列
|
69
74
|
scheduled_task_id=scheduled_task_id, # 传递scheduled_task_id
|
70
75
|
# worker_id和retry_count可以从其他地方获取
|
71
76
|
# 暂时使用默认值
|
@@ -112,7 +117,9 @@ class Task:
|
|
112
117
|
# 如果还有剩余的位置参数,添加到末尾(处理*args的情况)
|
113
118
|
if args_consumed < len(args_list):
|
114
119
|
final_args.extend(args_list[args_consumed:])
|
115
|
-
|
120
|
+
|
121
|
+
logger.debug(f"[TaskContext注入] 任务 {self.name} - 注入后参数: args={final_args}, kwargs={list(final_kwargs.keys())}")
|
122
|
+
|
116
123
|
return tuple(final_args), final_kwargs
|
117
124
|
|
118
125
|
def run(self, *args, **kwargs):
|
@@ -51,15 +51,15 @@ class UnifiedManagerBase(ABC):
|
|
51
51
|
def _detect_mode(self) -> bool:
|
52
52
|
"""
|
53
53
|
检测是单命名空间还是多命名空间模式
|
54
|
-
|
54
|
+
|
55
55
|
Returns:
|
56
56
|
True: 单命名空间模式
|
57
57
|
False: 多命名空间模式
|
58
58
|
"""
|
59
59
|
# 检查URL格式
|
60
|
-
# 单命名空间: /api/v1/namespaces/{name} 或 /api/namespaces/{name}
|
60
|
+
# 单命名空间: /api/v1/namespaces/{name} 或 /api/namespaces/{name} 或 /namespaces/{name}
|
61
61
|
# 多命名空间: 不包含这些路径或以 /api 结尾
|
62
|
-
|
62
|
+
|
63
63
|
# 检查新格式
|
64
64
|
if '/api/v1/namespaces/' in self.task_center_url:
|
65
65
|
# 提取命名空间名称
|
@@ -68,7 +68,7 @@ class UnifiedManagerBase(ABC):
|
|
68
68
|
self.namespace_name = match.group(1)
|
69
69
|
logger.info(f"检测到单命名空间模式: {self.namespace_name}")
|
70
70
|
return True
|
71
|
-
|
71
|
+
|
72
72
|
# 兼容旧格式
|
73
73
|
elif '/api/namespaces/' in self.task_center_url:
|
74
74
|
# 提取命名空间名称
|
@@ -77,7 +77,16 @@ class UnifiedManagerBase(ABC):
|
|
77
77
|
self.namespace_name = match.group(1)
|
78
78
|
logger.info(f"检测到单命名空间模式: {self.namespace_name}")
|
79
79
|
return True
|
80
|
-
|
80
|
+
|
81
|
+
# 支持简化格式(无/api前缀)
|
82
|
+
elif '/namespaces/' in self.task_center_url:
|
83
|
+
# 提取命名空间名称
|
84
|
+
match = re.search(r'/namespaces/([^/]+)/?$', self.task_center_url)
|
85
|
+
if match:
|
86
|
+
self.namespace_name = match.group(1)
|
87
|
+
logger.info(f"检测到单命名空间模式(简化格式): {self.namespace_name}")
|
88
|
+
return True
|
89
|
+
|
81
90
|
# 多命名空间模式
|
82
91
|
logger.info("检测到多命名空间模式")
|
83
92
|
return False
|
@@ -85,7 +94,7 @@ class UnifiedManagerBase(ABC):
|
|
85
94
|
def get_base_url(self) -> str:
|
86
95
|
"""
|
87
96
|
获取任务中心的基础URL
|
88
|
-
|
97
|
+
|
89
98
|
Returns:
|
90
99
|
基础URL(去除命名空间路径)
|
91
100
|
"""
|
@@ -97,12 +106,16 @@ class UnifiedManagerBase(ABC):
|
|
97
106
|
elif '/api/namespaces/' in self.task_center_url:
|
98
107
|
# 兼容旧格式
|
99
108
|
return self.task_center_url.split('/api/namespaces/')[0]
|
100
|
-
|
109
|
+
elif '/namespaces/' in self.task_center_url:
|
110
|
+
# 简化格式(无/api前缀)
|
111
|
+
# 从 http://localhost:8001/namespaces/test5 提取 http://localhost:8001
|
112
|
+
return self.task_center_url.split('/namespaces/')[0]
|
113
|
+
|
101
114
|
# 多命名空间模式,如果URL以 /api/v1/ 结尾,去掉 /api/v1/ 部分
|
102
115
|
if self.task_center_url.endswith('/api/v1/') or self.task_center_url.endswith('/api/v1'):
|
103
116
|
# 从 http://localhost:8001/api/v1/ 提取 http://localhost:8001
|
104
117
|
return self.task_center_url.rstrip('/').rsplit('/api/v1', 1)[0]
|
105
|
-
|
118
|
+
|
106
119
|
return self.task_center_url
|
107
120
|
|
108
121
|
def get_target_namespaces(self) -> Optional[Set[str]]:
|
@@ -144,18 +157,67 @@ class UnifiedManagerBase(ABC):
|
|
144
157
|
async with session.get(url) as response:
|
145
158
|
if response.status == 200:
|
146
159
|
data = await response.json()
|
147
|
-
|
160
|
+
|
161
|
+
# 处理新格式(带 config_mode 字段)
|
162
|
+
redis_config_mode = data.get('redis_config_mode', 'direct')
|
163
|
+
pg_config_mode = data.get('pg_config_mode', 'direct')
|
164
|
+
|
165
|
+
# 处理 Redis 配置
|
166
|
+
redis_config = {}
|
167
|
+
if redis_config_mode == 'nacos':
|
168
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
169
|
+
redis_nacos_key = data.get('redis_nacos_key')
|
170
|
+
if redis_nacos_key:
|
171
|
+
try:
|
172
|
+
from jettask.config.nacos_config import config as nacos_config
|
173
|
+
redis_url = nacos_config.get(redis_nacos_key)
|
174
|
+
if redis_url:
|
175
|
+
redis_config = {'url': redis_url}
|
176
|
+
except Exception as e:
|
177
|
+
logger.error(f"从 Nacos 获取 Redis 配置失败: {e}")
|
178
|
+
continue
|
179
|
+
else:
|
180
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
181
|
+
redis_url = data.get('redis_url')
|
182
|
+
if redis_url:
|
183
|
+
redis_config = {'url': redis_url}
|
184
|
+
|
185
|
+
# 处理 PostgreSQL 配置
|
186
|
+
pg_config = {}
|
187
|
+
if pg_config_mode == 'nacos':
|
188
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
189
|
+
pg_nacos_key = data.get('pg_nacos_key')
|
190
|
+
if pg_nacos_key:
|
191
|
+
try:
|
192
|
+
from jettask.config.nacos_config import config as nacos_config
|
193
|
+
pg_url = nacos_config.get(pg_nacos_key)
|
194
|
+
if pg_url:
|
195
|
+
pg_config = {'url': pg_url}
|
196
|
+
except Exception as e:
|
197
|
+
logger.warning(f"从 Nacos 获取 PG 配置失败: {e}")
|
198
|
+
# PostgreSQL 是可选的
|
199
|
+
else:
|
200
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
201
|
+
pg_url = data.get('pg_url')
|
202
|
+
if pg_url:
|
203
|
+
pg_config = {'url': pg_url}
|
204
|
+
|
205
|
+
# 兼容旧格式:如果没有 config_mode 字段(旧版 API)
|
206
|
+
if not redis_config:
|
207
|
+
redis_url = data.get('redis_url', '')
|
208
|
+
if redis_url:
|
209
|
+
redis_config = {'url': redis_url}
|
210
|
+
|
211
|
+
if not pg_config:
|
212
|
+
pg_url = data.get('pg_url', '')
|
213
|
+
if pg_url:
|
214
|
+
pg_config = {'url': pg_url}
|
215
|
+
|
148
216
|
# 跳过没有有效配置的命名空间
|
149
|
-
|
150
|
-
pg_url = data.get('pg_url', '')
|
151
|
-
|
152
|
-
if not redis_url or not pg_url:
|
217
|
+
if not redis_config or not pg_config:
|
153
218
|
# logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
|
154
219
|
continue
|
155
|
-
|
156
|
-
redis_config = {'url': redis_url}
|
157
|
-
pg_config = {'url': pg_url}
|
158
|
-
|
220
|
+
|
159
221
|
ns_info = {
|
160
222
|
'id': data.get('id', data['name']), # 如果没有id,使用name作为id
|
161
223
|
'name': data['name'],
|
@@ -179,18 +241,66 @@ class UnifiedManagerBase(ABC):
|
|
179
241
|
if response.status == 200:
|
180
242
|
data_list = await response.json()
|
181
243
|
for data in data_list:
|
182
|
-
#
|
244
|
+
# 处理新格式(带 config_mode 字段)
|
245
|
+
redis_config_mode = data.get('redis_config_mode', 'direct')
|
246
|
+
pg_config_mode = data.get('pg_config_mode', 'direct')
|
247
|
+
|
248
|
+
# 处理 Redis 配置
|
249
|
+
redis_config = {}
|
250
|
+
if redis_config_mode == 'nacos':
|
251
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
252
|
+
redis_nacos_key = data.get('redis_nacos_key')
|
253
|
+
if redis_nacos_key:
|
254
|
+
try:
|
255
|
+
from jettask.config.nacos_config import config as nacos_config
|
256
|
+
redis_url = nacos_config.get(redis_nacos_key)
|
257
|
+
if redis_url:
|
258
|
+
redis_config = {'url': redis_url}
|
259
|
+
except Exception as e:
|
260
|
+
logger.error(f"从 Nacos 获取 Redis 配置失败: {e}")
|
261
|
+
continue
|
262
|
+
else:
|
263
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
264
|
+
redis_url = data.get('redis_url')
|
265
|
+
if redis_url:
|
266
|
+
redis_config = {'url': redis_url}
|
267
|
+
|
268
|
+
# 处理 PostgreSQL 配置
|
269
|
+
pg_config = {}
|
270
|
+
if pg_config_mode == 'nacos':
|
271
|
+
# Nacos 模式 - 需要通过 nacos_config 获取真实 URL
|
272
|
+
pg_nacos_key = data.get('pg_nacos_key')
|
273
|
+
if pg_nacos_key:
|
274
|
+
try:
|
275
|
+
from jettask.config.nacos_config import config as nacos_config
|
276
|
+
pg_url = nacos_config.get(pg_nacos_key)
|
277
|
+
if pg_url:
|
278
|
+
pg_config = {'url': pg_url}
|
279
|
+
except Exception as e:
|
280
|
+
logger.warning(f"从 Nacos 获取 PG 配置失败: {e}")
|
281
|
+
# PostgreSQL 是可选的
|
282
|
+
else:
|
283
|
+
# Direct 模式 - 直接使用 API 返回的 URL
|
284
|
+
pg_url = data.get('pg_url')
|
285
|
+
if pg_url:
|
286
|
+
pg_config = {'url': pg_url}
|
287
|
+
|
288
|
+
# 兼容旧格式:如果没有 config_mode 字段(旧版 API)
|
289
|
+
if not redis_config:
|
290
|
+
redis_url = data.get('redis_url', '')
|
291
|
+
if redis_url:
|
292
|
+
redis_config = {'url': redis_url}
|
293
|
+
|
294
|
+
if not pg_config:
|
295
|
+
pg_url = data.get('pg_url', '')
|
296
|
+
if pg_url:
|
297
|
+
pg_config = {'url': pg_url}
|
298
|
+
|
183
299
|
# 跳过没有有效配置的命名空间
|
184
|
-
|
185
|
-
pg_url = data.get('pg_url', '')
|
186
|
-
|
187
|
-
if not redis_url or not pg_url:
|
300
|
+
if not redis_config or not pg_config:
|
188
301
|
# logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
|
189
302
|
continue
|
190
|
-
|
191
|
-
redis_config = {'url': redis_url}
|
192
|
-
pg_config = {'url': pg_url}
|
193
|
-
|
303
|
+
|
194
304
|
ns_info = {
|
195
305
|
'id': data.get('id', data['name']), # 如果没有id,使用name作为id
|
196
306
|
'name': data['name'],
|
jettask/db/__init__.py
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
"""
|
2
|
+
数据库模块
|
3
|
+
|
4
|
+
提供统一的数据库模型定义、操作接口和连接管理
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .base import Base, get_engine, get_session, init_db
|
8
|
+
from .connector import (
|
9
|
+
# 连接池函数
|
10
|
+
get_sync_redis_pool,
|
11
|
+
get_async_redis_pool,
|
12
|
+
get_async_redis_pool_for_pubsub,
|
13
|
+
get_pg_engine_and_factory,
|
14
|
+
get_asyncpg_pool,
|
15
|
+
# 客户端实例函数
|
16
|
+
get_sync_redis_client,
|
17
|
+
get_async_redis_client,
|
18
|
+
get_dual_mode_async_redis_client,
|
19
|
+
# 缓存清理
|
20
|
+
clear_all_cache,
|
21
|
+
# 配置解析
|
22
|
+
DBConfig,
|
23
|
+
# 连接器类
|
24
|
+
SyncRedisConnector,
|
25
|
+
RedisConnector,
|
26
|
+
PostgreSQLConnector,
|
27
|
+
ConnectionManager,
|
28
|
+
# 便捷函数
|
29
|
+
create_redis_client,
|
30
|
+
create_pg_session,
|
31
|
+
)
|
32
|
+
|
33
|
+
__all__ = [
|
34
|
+
# SQLAlchemy 基础
|
35
|
+
'Base',
|
36
|
+
'get_engine',
|
37
|
+
'get_session',
|
38
|
+
'init_db',
|
39
|
+
|
40
|
+
# 连接池函数
|
41
|
+
'get_sync_redis_pool',
|
42
|
+
'get_async_redis_pool',
|
43
|
+
'get_async_redis_pool_for_pubsub',
|
44
|
+
'get_pg_engine_and_factory',
|
45
|
+
'get_asyncpg_pool',
|
46
|
+
|
47
|
+
# 客户端实例函数
|
48
|
+
'get_sync_redis_client',
|
49
|
+
'get_async_redis_client',
|
50
|
+
'get_dual_mode_async_redis_client',
|
51
|
+
|
52
|
+
# 缓存清理
|
53
|
+
'clear_all_cache',
|
54
|
+
|
55
|
+
# 配置解析
|
56
|
+
'DBConfig',
|
57
|
+
|
58
|
+
# 连接器类
|
59
|
+
'SyncRedisConnector',
|
60
|
+
'RedisConnector',
|
61
|
+
'PostgreSQLConnector',
|
62
|
+
'ConnectionManager',
|
63
|
+
|
64
|
+
# 便捷函数
|
65
|
+
'create_redis_client',
|
66
|
+
'create_pg_session',
|
67
|
+
]
|