jettask 0.2.14__py3-none-any.whl → 0.2.16__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.
Files changed (148) hide show
  1. jettask/__init__.py +14 -35
  2. jettask/{webui/__main__.py → __main__.py} +4 -4
  3. jettask/api/__init__.py +103 -0
  4. jettask/api/v1/__init__.py +29 -0
  5. jettask/api/v1/alerts.py +226 -0
  6. jettask/api/v1/analytics.py +323 -0
  7. jettask/api/v1/namespaces.py +134 -0
  8. jettask/api/v1/overview.py +136 -0
  9. jettask/api/v1/queues.py +530 -0
  10. jettask/api/v1/scheduled.py +420 -0
  11. jettask/api/v1/settings.py +44 -0
  12. jettask/{webui/api.py → api.py} +4 -46
  13. jettask/{webui/backend → backend}/main.py +21 -109
  14. jettask/{webui/backend → backend}/main_unified.py +1 -1
  15. jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
  16. jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
  17. jettask/{webui/backend → backend}/unified_api_router.py +14 -74
  18. jettask/{core/cli.py → cli.py} +106 -26
  19. jettask/config/nacos_config.py +386 -0
  20. jettask/core/app.py +8 -100
  21. jettask/core/db_manager.py +515 -0
  22. jettask/core/event_pool.py +5 -2
  23. jettask/core/unified_manager_base.py +47 -14
  24. jettask/{webui/db_init.py → db_init.py} +1 -1
  25. jettask/executors/asyncio.py +2 -2
  26. jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
  27. jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
  28. jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
  29. jettask/{webui/run.py → run.py} +1 -1
  30. jettask/{webui/run_webui.py → run_webui.py} +4 -4
  31. jettask/scheduler/multi_namespace_scheduler.py +2 -2
  32. jettask/scheduler/unified_manager.py +5 -5
  33. jettask/scheduler/unified_scheduler_manager.py +1 -1
  34. jettask/schemas/__init__.py +166 -0
  35. jettask/schemas/alert.py +99 -0
  36. jettask/schemas/backlog.py +122 -0
  37. jettask/schemas/common.py +139 -0
  38. jettask/schemas/monitoring.py +181 -0
  39. jettask/schemas/namespace.py +168 -0
  40. jettask/schemas/queue.py +83 -0
  41. jettask/schemas/scheduled_task.py +128 -0
  42. jettask/schemas/task.py +70 -0
  43. jettask/services/__init__.py +24 -0
  44. jettask/services/alert_service.py +454 -0
  45. jettask/services/analytics_service.py +46 -0
  46. jettask/services/overview_service.py +978 -0
  47. jettask/services/queue_service.py +711 -0
  48. jettask/services/redis_monitor_service.py +151 -0
  49. jettask/services/scheduled_task_service.py +207 -0
  50. jettask/services/settings_service.py +758 -0
  51. jettask/services/task_service.py +157 -0
  52. jettask/{webui/task_center.py → task_center.py} +30 -8
  53. jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
  54. jettask/{webui/config.py → webui_config.py} +6 -1
  55. jettask/webui_exceptions.py +67 -0
  56. jettask/webui_sql/verify_database.sql +72 -0
  57. {jettask-0.2.14.dist-info → jettask-0.2.16.dist-info}/METADATA +3 -1
  58. jettask-0.2.16.dist-info/RECORD +150 -0
  59. {jettask-0.2.14.dist-info → jettask-0.2.16.dist-info}/entry_points.txt +1 -1
  60. jettask/webui/backend/data_api.py +0 -3294
  61. jettask/webui/backend/namespace_api.py +0 -295
  62. jettask/webui/backend/queue_backlog_api.py +0 -727
  63. jettask/webui/backend/redis_monitor_api.py +0 -476
  64. jettask/webui/frontend/index.html +0 -13
  65. jettask/webui/frontend/package.json +0 -30
  66. jettask/webui/frontend/src/App.css +0 -109
  67. jettask/webui/frontend/src/App.jsx +0 -66
  68. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  69. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  70. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  71. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  72. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  73. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  74. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  75. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  76. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  77. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  78. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  79. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  80. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  81. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  82. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  83. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  84. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  85. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  86. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  87. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  88. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  89. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  90. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  91. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  92. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  93. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  94. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  95. jettask/webui/frontend/src/index.css +0 -114
  96. jettask/webui/frontend/src/main.jsx +0 -22
  97. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  98. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  99. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  100. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  101. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  102. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  103. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  104. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
  105. jettask/webui/frontend/src/pages/Settings.jsx +0 -801
  106. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  107. jettask/webui/frontend/src/services/api.js +0 -159
  108. jettask/webui/frontend/src/services/queueTrend.js +0 -166
  109. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  110. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  111. jettask/webui/frontend/vite.config.js +0 -26
  112. jettask/webui/sql/init_database.sql +0 -640
  113. jettask-0.2.14.dist-info/RECORD +0 -172
  114. /jettask/{webui/backend → backend}/__init__.py +0 -0
  115. /jettask/{webui/backend → backend}/api/__init__.py +0 -0
  116. /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
  117. /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
  118. /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
  119. /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
  120. /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
  121. /jettask/{webui/backend → backend}/config.py +0 -0
  122. /jettask/{webui/backend → backend}/core/__init__.py +0 -0
  123. /jettask/{webui/backend → backend}/core/cache.py +0 -0
  124. /jettask/{webui/backend → backend}/core/database.py +0 -0
  125. /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
  126. /jettask/{webui/backend → backend}/data_access.py +0 -0
  127. /jettask/{webui/backend → backend}/dependencies.py +0 -0
  128. /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
  129. /jettask/{webui/backend → backend}/main_v2.py +0 -0
  130. /jettask/{webui/backend → backend}/models/__init__.py +0 -0
  131. /jettask/{webui/backend → backend}/models/requests.py +0 -0
  132. /jettask/{webui/backend → backend}/models/responses.py +0 -0
  133. /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
  134. /jettask/{webui/backend → backend}/services/__init__.py +0 -0
  135. /jettask/{webui/backend → backend}/start.py +0 -0
  136. /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
  137. /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
  138. /jettask/{webui/__init__.py → main.py} +0 -0
  139. /jettask/{webui/models.py → models.py} +0 -0
  140. /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
  141. /jettask/{webui/schema.sql → schema.sql} +0 -0
  142. /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
  143. /jettask/{webui/models → webui_models}/__init__.py +0 -0
  144. /jettask/{webui/models → webui_models}/namespace.py +0 -0
  145. /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
  146. {jettask-0.2.14.dist-info → jettask-0.2.16.dist-info}/WHEEL +0 -0
  147. {jettask-0.2.14.dist-info → jettask-0.2.16.dist-info}/licenses/LICENSE +0 -0
  148. {jettask-0.2.14.dist-info → jettask-0.2.16.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 配置管理模块
5
+ 使用 Nacos 作为配置中心
6
+ 支持定时刷新和配置变更监听
7
+ """
8
+ import os
9
+ import json
10
+ import yaml
11
+ import threading
12
+ from datetime import datetime
13
+ from typing import Dict, Any, Callable, List
14
+ from dotenv import load_dotenv
15
+ from nacos import NacosClient
16
+ import logging
17
+
18
+
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class Config:
24
+ """配置管理类,支持定时刷新和配置监听"""
25
+
26
+ def __init__(self, refresh_interval: int = 30):
27
+ """初始化配置管理器
28
+
29
+ Args:
30
+ refresh_interval: 配置刷新间隔(秒),默认30秒
31
+ """
32
+ self.nacos_config = None
33
+
34
+ self.nacos_group = None
35
+ self.nacos_data_id = None
36
+ self.local_dev_mode = None
37
+ # 服务注册信息
38
+ self.service_info = None
39
+
40
+ # 刷新相关
41
+ self.refresh_interval = refresh_interval
42
+ self.refresh_thread = None
43
+ self.stop_refresh = threading.Event()
44
+ self.last_refresh_time = None
45
+ self.config_version = 0
46
+
47
+ # 配置变更监听器
48
+ self.change_listeners: List[Callable[[Dict[str, Any]], None]] = []
49
+
50
+ # Nacos客户端(保持长连接)
51
+ self.nacos_client = None
52
+
53
+ # 配置锁,保证线程安全
54
+ self._config_lock = threading.RLock()
55
+
56
+ # 初始化配置
57
+ self._init_config()
58
+
59
+ self._config = self._load_config()
60
+
61
+
62
+ # 启动定时刷新
63
+ if not self.local_dev_mode and refresh_interval > 0:
64
+ self.start_refresh_thread()
65
+
66
+ @property
67
+ def config(self) -> dict:
68
+ """获取当前配置(线程安全)"""
69
+ with self._config_lock:
70
+ if not hasattr(self, '_config'):
71
+ self._config = self._load_config()
72
+ return self._config
73
+
74
+ def _init_config(self):
75
+ """初始化配置"""
76
+ try:
77
+ # 加载 .env 文件
78
+ load_dotenv()
79
+ self.nacos_config = {
80
+ 'server_addresses': os.getenv('NACOS_SERVER', '127.0.0.1:8848'),
81
+ 'namespace': os.getenv('NACOS_NAMESPACE', 'tamar_console_dev'),
82
+ 'username': os.getenv('NACOS_USERNAME', 'nacos'),
83
+ 'password': os.getenv('NACOS_PASSWORD', 'nacos')
84
+ }
85
+
86
+ self.nacos_group = os.getenv('NACOS_GROUP', 'DEFAULT_GROUP')
87
+ self.nacos_data_id = os.getenv('NACOS_DATA_ID', 'tamar-console-dev')
88
+ self.local_dev_mode = os.getenv('LOCAL_DEV_MODE', 'true').lower() == 'true'
89
+
90
+ # 服务注册信息
91
+ self.service_info = {
92
+ 'name': os.getenv('SERVICE_NAME', 'tamar-console'),
93
+ 'domain': os.getenv('SERVICE_DOMAIN', '127.0.0.1'),
94
+ 'port': int(os.getenv('SERVICE_PORT', '8002'))
95
+ }
96
+
97
+ # 创建 Nacos 客户端(保持连接)
98
+ if not self.nacos_client:
99
+ self.nacos_client = NacosClient(
100
+ self.nacos_config['server_addresses'],
101
+ namespace=self.nacos_config['namespace'],
102
+ username=self.nacos_config['username'],
103
+ password=self.nacos_config['password']
104
+ )
105
+ logger.info(f"Nacos客户端初始化成功: {self.nacos_config['server_addresses']}")
106
+ except Exception as e:
107
+ logger.error(f"初始化配置失败: {e}")
108
+ raise
109
+
110
+ def _load_config(self):
111
+ """加载配置"""
112
+ try:
113
+ # 尝试从 Nacos 加载配置
114
+ config = self._load_from_nacos()
115
+ self.last_refresh_time = datetime.now()
116
+ self.config_version += 1
117
+ logger.info(f"配置加载成功,版本: {self.config_version}")
118
+ return config
119
+ except Exception as e:
120
+ logger.error(f"从 Nacos 加载配置失败: {e}")
121
+ # 如果是首次加载失败,抛出异常
122
+ if not hasattr(self, '_config'):
123
+ raise
124
+ # 如果是刷新失败,保持现有配置
125
+ logger.warning("配置刷新失败,保持现有配置")
126
+ return self._config
127
+
128
+ def _load_from_nacos(self):
129
+ """从 Nacos 加载配置"""
130
+ # if self.local_dev_mode:
131
+ # # 本地开发模式,使用默认配置
132
+ # logger.info("本地开发模式,使用默认配置")
133
+ # return self._get_default_config()
134
+
135
+ if not self.nacos_client:
136
+ raise ValueError("Nacos客户端未初始化")
137
+
138
+ # 获取配置
139
+ config_str = self.nacos_client.get_config(self.nacos_data_id, self.nacos_group)
140
+
141
+ if not config_str:
142
+ logger.warning(f"Nacos配置为空: {self.nacos_data_id}/{self.nacos_group}")
143
+ return self._get_default_config()
144
+
145
+ # 解析配置(支持 Properties、YAML 和 JSON)
146
+ if self._is_properties_format(config_str):
147
+ config = self._parse_properties(config_str)
148
+ else:
149
+ # 尝试 YAML 或 JSON
150
+ try:
151
+ config = yaml.safe_load(config_str)
152
+ except:
153
+ try:
154
+ config = json.loads(config_str)
155
+ except:
156
+ logger.error(f"无法解析配置格式: {config_str[:100]}...")
157
+ raise
158
+ logger.debug(f"成功加载配置,包含 {len(config)} 个配置项")
159
+ return config
160
+
161
+ def _get_default_config(self) -> Dict[str, Any]:
162
+ """获取默认配置"""
163
+ return {
164
+ 'PG_DB_HOST': 'localhost',
165
+ 'PG_DB_PORT': 5432,
166
+ 'PG_DB_USERNAME': 'jettask',
167
+ 'PG_DB_PASSWORD': '123456',
168
+ 'PG_DB_DATABASE': 'jettask',
169
+ 'REDIS_HOST': 'localhost',
170
+ 'REDIS_PORT': 6379,
171
+ 'REDIS_DB': 0,
172
+ 'REDIS_PASSWORD': None
173
+ }
174
+
175
+ def _is_properties_format(self, config_str):
176
+ """判断是否为 Properties 格式"""
177
+ lines = config_str.strip().split('\n')
178
+ for line in lines:
179
+ line = line.strip()
180
+ if line and not line.startswith('#'):
181
+ return '=' in line
182
+ return False
183
+
184
+ def _parse_properties(self, config_str):
185
+ """解析 Properties 格式配置"""
186
+ config = {}
187
+ lines = config_str.strip().split('\n')
188
+
189
+ for line in lines:
190
+ line = line.strip()
191
+ # 跳过空行和注释
192
+ if not line or line.startswith('#'):
193
+ continue
194
+
195
+ # 解析键值对
196
+ if '=' in line:
197
+ key, value = line.split('=', 1)
198
+ key = key.strip()
199
+ value = value.strip()
200
+
201
+ # 转换为嵌套字典
202
+ parts = key.split('.')
203
+ current = config
204
+ for i, part in enumerate(parts[:-1]):
205
+ if part not in current:
206
+ current[part] = {}
207
+ current = current[part]
208
+
209
+ # 尝试转换数据类型
210
+ final_value = value
211
+ if value.lower() == 'true':
212
+ final_value = True
213
+ elif value.lower() == 'false':
214
+ final_value = False
215
+ elif value.isdigit():
216
+ final_value = int(value)
217
+ elif self._is_float(value):
218
+ final_value = float(value)
219
+
220
+ current[parts[-1]] = final_value
221
+
222
+ return config
223
+
224
+ def _is_float(self, value):
225
+ """判断字符串是否为浮点数"""
226
+ try:
227
+ float(value)
228
+ return '.' in value
229
+ except:
230
+ return False
231
+
232
+ def get_database_url(self):
233
+ """构建数据库连接字符串(FastAPI 使用)"""
234
+ return self._get_database_uri()
235
+
236
+ def _get_database_uri(self):
237
+ """构建数据库连接字符串"""
238
+ config = self.config
239
+ host = config.get("PG_DB_HOST")
240
+ port = config.get("PG_DB_PORT")
241
+ username = config.get("PG_DB_USERNAME")
242
+ password = config.get("PG_DB_PASSWORD")
243
+ database = config.get("PG_DB_DATABASE")
244
+ return f'postgresql+asyncpg://{username}:{password}@{host}:{port}/{database}'
245
+
246
+ def get_redis_connection(self):
247
+ """获取 Redis 异步连接实例"""
248
+ config = self.config
249
+
250
+ redis_host = config.get("REDIS_HOST", "localhost")
251
+ redis_port = int(config.get("REDIS_PORT", "6379"))
252
+ redis_db = int(config.get("REDIS_DB", "0"))
253
+ redis_password = config.get("REDIS_PASSWORD") or None
254
+ redis_user = config.get("REDIS_USER") or None
255
+
256
+ # 构建 redis:// 或 rediss:// 的 URL(注意 password 可能为空)
257
+ if redis_user:
258
+ redis_url = f"redis://{redis_user}:{redis_password}@{redis_host}:{redis_port}/{redis_db}"
259
+ elif redis_password:
260
+ redis_url = f"redis://:{redis_password}@{redis_host}:{redis_port}/{redis_db}"
261
+ else:
262
+ redis_url = f"redis://{redis_host}:{redis_port}/{redis_db}"
263
+ print(f'{redis_url=}')
264
+ return redis_url
265
+
266
+ def get(self, key, default=None):
267
+ """获取配置项(线程安全)"""
268
+ with self._config_lock:
269
+ return self.config.get(key, default)
270
+
271
+ def refresh(self) -> bool:
272
+ """手动刷新配置
273
+
274
+ Returns:
275
+ True if config was refreshed, False otherwise
276
+ """
277
+ try:
278
+ logger.info("手动触发配置刷新")
279
+ old_config = self._config.copy() if hasattr(self, '_config') else {}
280
+
281
+ with self._config_lock:
282
+ new_config = self._load_config()
283
+ self._config = new_config
284
+
285
+ # 检查配置是否有变化
286
+ if old_config != new_config:
287
+ logger.info("配置已更新,通知监听器")
288
+ self._notify_listeners(new_config)
289
+ return True
290
+ else:
291
+ logger.debug("配置无变化")
292
+ return False
293
+ except Exception as e:
294
+ logger.error(f"刷新配置失败: {e}")
295
+ return False
296
+
297
+ def start_refresh_thread(self):
298
+ """启动定时刷新线程"""
299
+ if self.refresh_thread and self.refresh_thread.is_alive():
300
+ logger.warning("刷新线程已在运行")
301
+ return
302
+
303
+ self.stop_refresh.clear()
304
+ self.refresh_thread = threading.Thread(
305
+ target=self._refresh_loop,
306
+ name="NacosConfigRefresh",
307
+ daemon=True
308
+ )
309
+ self.refresh_thread.start()
310
+ logger.info(f"配置刷新线程已启动,刷新间隔: {self.refresh_interval}秒")
311
+
312
+ def stop_refresh_thread(self):
313
+ """停止定时刷新线程"""
314
+ if self.refresh_thread and self.refresh_thread.is_alive():
315
+ logger.info("正在停止配置刷新线程...")
316
+ self.stop_refresh.set()
317
+ self.refresh_thread.join(timeout=5)
318
+ logger.info("配置刷新线程已停止")
319
+
320
+ def _refresh_loop(self):
321
+ """刷新循环"""
322
+ logger.info("配置刷新循环已开始")
323
+
324
+ while not self.stop_refresh.is_set():
325
+ try:
326
+ # 等待指定的刷新间隔
327
+ if self.stop_refresh.wait(self.refresh_interval):
328
+ break
329
+
330
+ # 执行刷新
331
+ self.refresh()
332
+
333
+ except Exception as e:
334
+ logger.error(f"配置刷新循环异常: {e}")
335
+ # 发生异常后等待一段时间再重试
336
+ if self.stop_refresh.wait(10):
337
+ break
338
+
339
+ logger.info("配置刷新循环已结束")
340
+
341
+ def add_change_listener(self, listener: Callable[[Dict[str, Any]], None]):
342
+ """添加配置变更监听器
343
+
344
+ Args:
345
+ listener: 配置变更时的回调函数,接收新配置作为参数
346
+ """
347
+ if listener not in self.change_listeners:
348
+ self.change_listeners.append(listener)
349
+ logger.info(f"添加配置变更监听器: {listener.__name__}")
350
+
351
+ def remove_change_listener(self, listener: Callable[[Dict[str, Any]], None]):
352
+ """移除配置变更监听器"""
353
+ if listener in self.change_listeners:
354
+ self.change_listeners.remove(listener)
355
+ logger.info(f"移除配置变更监听器: {listener.__name__}")
356
+
357
+ def _notify_listeners(self, new_config: Dict[str, Any]):
358
+ """通知所有监听器配置已变更"""
359
+ for listener in self.change_listeners:
360
+ try:
361
+ listener(new_config)
362
+ except Exception as e:
363
+ logger.error(f"配置变更监听器执行失败 {listener.__name__}: {e}")
364
+
365
+ def get_config_info(self) -> Dict[str, Any]:
366
+ """获取配置信息"""
367
+ return {
368
+ 'version': self.config_version,
369
+ 'last_refresh_time': self.last_refresh_time.isoformat() if self.last_refresh_time else None,
370
+ 'refresh_interval': self.refresh_interval,
371
+ 'refresh_thread_active': self.refresh_thread.is_alive() if self.refresh_thread else False,
372
+ 'nacos_server': self.nacos_config['server_addresses'] if self.nacos_config else None,
373
+ 'nacos_namespace': self.nacos_config['namespace'] if self.nacos_config else None,
374
+ 'nacos_group': self.nacos_group,
375
+ 'nacos_data_id': self.nacos_data_id,
376
+ 'local_dev_mode': self.local_dev_mode,
377
+ 'config_items_count': len(self.config) if hasattr(self, '_config') else 0
378
+ }
379
+
380
+ def __del__(self):
381
+ """析构函数,确保线程正确关闭"""
382
+ self.stop_refresh_thread()
383
+
384
+
385
+ # 创建全局配置实例(默认30秒刷新一次)
386
+ config = Config(refresh_interval=30)
jettask/core/app.py CHANGED
@@ -298,76 +298,7 @@ class Jettask(object):
298
298
  self._should_exit = False
299
299
  self._worker_started = False
300
300
  self._handlers_registered = False
301
-
302
- async def _load_config_from_task_center_async(self):
303
- """异步加载任务中心配置"""
304
- try:
305
- if not self.task_center:
306
- return False
307
-
308
- # 连接并获取配置
309
- connected = await self.task_center.connect()
310
- if not connected:
311
- return False
312
-
313
- config = {
314
- 'redis_config': self.task_center.redis_config,
315
- 'pg_config': self.task_center.pg_config,
316
- 'namespace_name': self.task_center.namespace_name,
317
- 'version': self.task_center.version
318
- }
319
-
320
- if config['redis_config']:
321
- # 任务中心配置优先级高于手动配置
322
- redis_config = config.get('redis_config', {})
323
- pg_config = config.get('pg_config', {})
324
- # 构建Redis URL
325
- if redis_config:
326
- redis_host = redis_config.get('host', 'localhost')
327
- redis_port = redis_config.get('port', 6379)
328
- redis_password = redis_config.get('password')
329
- redis_db = redis_config.get('db', 0)
330
-
331
- if redis_password:
332
- self.redis_url = f"redis://:{redis_password}@{redis_host}:{redis_port}/{redis_db}"
333
- else:
334
- self.redis_url = f"redis://{redis_host}:{redis_port}/{redis_db}"
335
-
336
- logger.info(f"从任务中心加载Redis配置: {redis_host}:{redis_port}/{redis_db}")
337
-
338
- # 构建PostgreSQL URL
339
- if pg_config:
340
- pg_host = pg_config.get('host', 'localhost')
341
- pg_port = pg_config.get('port', 5432)
342
- pg_user = pg_config.get('user', 'postgres')
343
- pg_password = pg_config.get('password', '')
344
- pg_database = pg_config.get('database', 'jettask')
345
-
346
- self.pg_url = f"postgresql://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_database}"
347
- logger.info(f"从任务中心加载PostgreSQL配置: {pg_host}:{pg_port}/{pg_database}")
348
-
349
- # 保存配置供后续使用
350
- self._task_center_config = config
351
-
352
- # 更新Redis前缀为命名空间名称
353
- if self.task_center and self.task_center.redis_prefix != "jettask":
354
- self.redis_prefix = self.task_center.redis_prefix
355
- # 更新相关前缀
356
- self.STATUS_PREFIX = f"{self.redis_prefix}:STATUS:"
357
- self.RESULT_PREFIX = f"{self.redis_prefix}:RESULT:"
358
-
359
- # 清理已缓存的Redis连接,强制重新创建
360
- if hasattr(self, '_redis'):
361
- delattr(self, '_redis')
362
- if hasattr(self, '_async_redis'):
363
- delattr(self, '_async_redis')
364
- if hasattr(self, '_ep'):
365
- delattr(self, '_ep')
366
-
367
- return True
368
- except Exception as e:
369
- logger.warning(f"从任务中心加载配置失败: {e}")
370
- return False
301
+
371
302
 
372
303
  def _load_config_from_task_center(self):
373
304
  """从任务中心加载配置"""
@@ -461,7 +392,7 @@ class Jettask(object):
461
392
  task_center: TaskCenter实例
462
393
 
463
394
  使用示例:
464
- from jettask.webui.task_center import TaskCenter
395
+ from jettask.task_center import TaskCenter
465
396
 
466
397
  # 创建任务中心客户端(可复用)
467
398
  task_center = TaskCenter("http://localhost:8001/api/namespaces/demo")
@@ -510,24 +441,6 @@ class Jettask(object):
510
441
  'version': task_center.version
511
442
  }
512
443
 
513
- async def init_from_task_center(self):
514
- """
515
- 从任务中心初始化配置(异步方法)
516
-
517
- 在异步环境中发送任务前调用此方法,确保配置已加载
518
-
519
- 使用示例:
520
- from jettask.webui.task_center import TaskCenter
521
-
522
- task_center = TaskCenter("http://localhost:8001/api/namespaces/demo")
523
- app = Jettask()
524
- app.mount_task_center(task_center)
525
- await app.init_from_task_center() # 会自动连接task_center
526
- await task.apply_async(...)
527
- """
528
- if self.task_center and self.task_center.is_enabled and not self._task_center_config:
529
- return await self._load_config_from_task_center_async()
530
- return True
531
444
 
532
445
  def _setup_cleanup_handlers(self):
533
446
  """设置清理处理器"""
@@ -619,9 +532,9 @@ class Jettask(object):
619
532
  return getattr(self, name)
620
533
 
621
534
  # 如果配置了任务中心且还未加载配置,先加载配置
622
- if self.task_center and self.task_center.is_enabled and not self._task_center_config:
623
- self._load_config_from_task_center()
624
-
535
+ # if self.task_center and self.task_center.is_enabled and not self._task_center_config:
536
+ # self._load_config_from_task_center()
537
+ print(f'{self.redis_url=}')
625
538
  pool = get_redis_pool(self.redis_url, self.max_connections)
626
539
  redis_cli = redis.StrictRedis(connection_pool=pool)
627
540
  setattr(self, name, redis_cli)
@@ -1059,12 +972,7 @@ class Jettask(object):
1059
972
 
1060
973
  event_queue = deque()
1061
974
 
1062
- # 创建消费者组
1063
- try:
1064
- self.ep.create_group()
1065
- except Exception as e:
1066
- logger.warning(f"创建消费者组时出错: {e}")
1067
- # 继续执行,listening_event会自动处理
975
+ # 消费者组会在listening_event方法内部自动创建
1068
976
 
1069
977
  # 根据执行器类型创建对应的执行器
1070
978
  if execute_type == "asyncio":
@@ -1138,8 +1046,8 @@ class Jettask(object):
1138
1046
  # 标记worker已启动
1139
1047
  self._worker_started = True
1140
1048
 
1141
- # 如果配置了任务中心,从任务中心获取配置
1142
- if self.task_center and self.task_center.is_enabled:
1049
+ # 如果配置了任务中心且配置尚未加载,从任务中心获取配置
1050
+ if self.task_center and self.task_center.is_enabled and not self._task_center_config:
1143
1051
  self._load_config_from_task_center()
1144
1052
 
1145
1053
  # 注册清理处理器(只在启动worker时注册)