jettask 0.2.19__py3-none-any.whl → 0.2.20__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 +10 -3
- jettask/cli.py +314 -228
- jettask/config/__init__.py +9 -1
- jettask/config/config.py +245 -0
- jettask/config/env_loader.py +381 -0
- jettask/config/lua_scripts.py +158 -0
- jettask/config/nacos_config.py +132 -5
- jettask/core/__init__.py +1 -1
- jettask/core/app.py +1573 -666
- jettask/core/app_importer.py +33 -16
- jettask/core/container.py +532 -0
- jettask/core/task.py +1 -4
- jettask/core/unified_manager_base.py +2 -2
- jettask/executor/__init__.py +38 -0
- jettask/executor/core.py +625 -0
- jettask/executor/executor.py +338 -0
- jettask/executor/orchestrator.py +290 -0
- jettask/executor/process_entry.py +638 -0
- jettask/executor/task_executor.py +317 -0
- jettask/messaging/__init__.py +68 -0
- jettask/messaging/event_pool.py +2188 -0
- jettask/messaging/reader.py +519 -0
- jettask/messaging/registry.py +266 -0
- jettask/messaging/scanner.py +369 -0
- jettask/messaging/sender.py +312 -0
- jettask/persistence/__init__.py +118 -0
- jettask/persistence/backlog_monitor.py +567 -0
- jettask/{backend/data_access.py → persistence/base.py} +58 -57
- jettask/persistence/consumer.py +315 -0
- jettask/{core → persistence}/db_manager.py +23 -22
- jettask/persistence/maintenance.py +81 -0
- jettask/persistence/message_consumer.py +259 -0
- jettask/{backend/namespace_data_access.py → persistence/namespace.py} +66 -98
- jettask/persistence/offline_recovery.py +196 -0
- jettask/persistence/queue_discovery.py +215 -0
- jettask/persistence/task_persistence.py +218 -0
- jettask/persistence/task_updater.py +583 -0
- jettask/scheduler/__init__.py +2 -2
- jettask/scheduler/loader.py +6 -5
- jettask/scheduler/run_scheduler.py +1 -1
- jettask/scheduler/scheduler.py +7 -7
- jettask/scheduler/{unified_scheduler_manager.py → scheduler_coordinator.py} +18 -13
- jettask/task/__init__.py +16 -0
- jettask/{router.py → task/router.py} +26 -8
- jettask/task/task_center/__init__.py +9 -0
- jettask/task/task_executor.py +318 -0
- jettask/task/task_registry.py +291 -0
- jettask/test_connection_monitor.py +73 -0
- jettask/utils/__init__.py +31 -1
- jettask/{monitor/run_backlog_collector.py → utils/backlog_collector.py} +1 -1
- jettask/utils/db_connector.py +1629 -0
- jettask/{db_init.py → utils/db_init.py} +1 -1
- jettask/utils/rate_limit/__init__.py +30 -0
- jettask/utils/rate_limit/concurrency_limiter.py +665 -0
- jettask/utils/rate_limit/config.py +145 -0
- jettask/utils/rate_limit/limiter.py +41 -0
- jettask/utils/rate_limit/manager.py +269 -0
- jettask/utils/rate_limit/qps_limiter.py +154 -0
- jettask/utils/rate_limit/task_limiter.py +384 -0
- jettask/utils/serializer.py +3 -0
- jettask/{monitor/stream_backlog_monitor.py → utils/stream_backlog.py} +14 -6
- jettask/utils/time_sync.py +173 -0
- jettask/webui/__init__.py +27 -0
- jettask/{api/v1 → webui/api}/alerts.py +1 -1
- jettask/{api/v1 → webui/api}/analytics.py +2 -2
- jettask/{api/v1 → webui/api}/namespaces.py +1 -1
- jettask/{api/v1 → webui/api}/overview.py +1 -1
- jettask/{api/v1 → webui/api}/queues.py +3 -3
- jettask/{api/v1 → webui/api}/scheduled.py +1 -1
- jettask/{api/v1 → webui/api}/settings.py +1 -1
- jettask/{api.py → webui/app.py} +253 -145
- jettask/webui/namespace_manager/__init__.py +10 -0
- jettask/{multi_namespace_consumer.py → webui/namespace_manager/multi.py} +69 -22
- jettask/{unified_consumer_manager.py → webui/namespace_manager/unified.py} +1 -1
- jettask/{run.py → webui/run.py} +2 -2
- jettask/{services → webui/services}/__init__.py +1 -3
- jettask/{services → webui/services}/overview_service.py +34 -16
- jettask/{services → webui/services}/queue_service.py +1 -1
- jettask/{backend → webui/services}/queue_stats_v2.py +1 -1
- jettask/{services → webui/services}/settings_service.py +1 -1
- jettask/worker/__init__.py +53 -0
- jettask/worker/lifecycle.py +1507 -0
- jettask/worker/manager.py +583 -0
- jettask/{core/offline_worker_recovery.py → worker/recovery.py} +268 -175
- {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/METADATA +2 -71
- jettask-0.2.20.dist-info/RECORD +145 -0
- jettask/__main__.py +0 -140
- jettask/api/__init__.py +0 -103
- jettask/backend/__init__.py +0 -1
- jettask/backend/api/__init__.py +0 -3
- jettask/backend/api/v1/__init__.py +0 -17
- jettask/backend/api/v1/monitoring.py +0 -431
- jettask/backend/api/v1/namespaces.py +0 -504
- jettask/backend/api/v1/queues.py +0 -342
- jettask/backend/api/v1/tasks.py +0 -367
- jettask/backend/core/__init__.py +0 -3
- jettask/backend/core/cache.py +0 -221
- jettask/backend/core/database.py +0 -200
- jettask/backend/core/exceptions.py +0 -102
- jettask/backend/dependencies.py +0 -261
- jettask/backend/init_meta_db.py +0 -158
- jettask/backend/main.py +0 -1426
- jettask/backend/main_unified.py +0 -78
- jettask/backend/main_v2.py +0 -394
- jettask/backend/models/__init__.py +0 -3
- jettask/backend/models/requests.py +0 -236
- jettask/backend/models/responses.py +0 -230
- jettask/backend/namespace_api_old.py +0 -267
- jettask/backend/services/__init__.py +0 -3
- jettask/backend/start.py +0 -42
- jettask/backend/unified_api_router.py +0 -1541
- jettask/cleanup_deprecated_tables.sql +0 -16
- jettask/core/consumer_manager.py +0 -1695
- jettask/core/delay_scanner.py +0 -256
- jettask/core/event_pool.py +0 -1700
- jettask/core/heartbeat_process.py +0 -222
- jettask/core/task_batch.py +0 -153
- jettask/core/worker_scanner.py +0 -271
- jettask/executors/__init__.py +0 -5
- jettask/executors/asyncio.py +0 -876
- jettask/executors/base.py +0 -30
- jettask/executors/common.py +0 -148
- jettask/executors/multi_asyncio.py +0 -309
- jettask/gradio_app.py +0 -570
- jettask/integrated_gradio_app.py +0 -1088
- jettask/main.py +0 -0
- jettask/monitoring/__init__.py +0 -3
- jettask/pg_consumer.py +0 -1896
- jettask/run_monitor.py +0 -22
- jettask/run_webui.py +0 -148
- jettask/scheduler/multi_namespace_scheduler.py +0 -294
- jettask/scheduler/unified_manager.py +0 -450
- jettask/task_center_client.py +0 -150
- jettask/utils/serializer_optimized.py +0 -33
- jettask/webui_exceptions.py +0 -67
- jettask-0.2.19.dist-info/RECORD +0 -150
- /jettask/{constants.py → config/constants.py} +0 -0
- /jettask/{backend/config.py → config/task_center.py} +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/pg_consumer_v2.py +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql/add_execution_time_field.sql +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql/create_new_tables.sql +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql/create_tables_v3.sql +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql/migrate_to_new_structure.sql +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql/modify_time_fields.sql +0 -0
- /jettask/{pg_consumer → messaging/pg_consumer}/sql_utils.py +0 -0
- /jettask/{models.py → persistence/models.py} +0 -0
- /jettask/scheduler/{manager.py → task_crud.py} +0 -0
- /jettask/{schema.sql → schemas/schema.sql} +0 -0
- /jettask/{task_center.py → task/task_center/client.py} +0 -0
- /jettask/{monitoring → utils}/file_watcher.py +0 -0
- /jettask/{services/redis_monitor_service.py → utils/redis_monitor.py} +0 -0
- /jettask/{api/v1 → webui/api}/__init__.py +0 -0
- /jettask/{webui_config.py → webui/config.py} +0 -0
- /jettask/{webui_models → webui/models}/__init__.py +0 -0
- /jettask/{webui_models → webui/models}/namespace.py +0 -0
- /jettask/{services → webui/services}/alert_service.py +0 -0
- /jettask/{services → webui/services}/analytics_service.py +0 -0
- /jettask/{services → webui/services}/scheduled_task_service.py +0 -0
- /jettask/{services → webui/services}/task_service.py +0 -0
- /jettask/{webui_sql → webui/sql}/batch_upsert_functions.sql +0 -0
- /jettask/{webui_sql → webui/sql}/verify_database.sql +0 -0
- {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/WHEEL +0 -0
- {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/top_level.txt +0 -0
jettask/run_monitor.py
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
"""启动任务监控服务"""
|
3
|
-
|
4
|
-
import uvicorn
|
5
|
-
import sys
|
6
|
-
import os
|
7
|
-
|
8
|
-
# 添加当前目录到Python路径
|
9
|
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
10
|
-
|
11
|
-
if __name__ == "__main__":
|
12
|
-
print("Starting JetTask Monitor Backend...")
|
13
|
-
print("API will be available at: http://localhost:8000")
|
14
|
-
print("API docs: http://localhost:8000/docs")
|
15
|
-
|
16
|
-
uvicorn.run(
|
17
|
-
"backend.main:app",
|
18
|
-
host="0.0.0.0",
|
19
|
-
port=8000,
|
20
|
-
reload=True,
|
21
|
-
reload_dirs=[os.path.dirname(os.path.abspath(__file__))]
|
22
|
-
)
|
jettask/run_webui.py
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
JetTask WebUI 启动脚本
|
4
|
-
支持多种界面模式:
|
5
|
-
1. FastAPI + HTML (原始模式)
|
6
|
-
2. Gradio 界面 (新模式)
|
7
|
-
"""
|
8
|
-
|
9
|
-
import argparse
|
10
|
-
import asyncio
|
11
|
-
import logging
|
12
|
-
import os
|
13
|
-
import sys
|
14
|
-
from pathlib import Path
|
15
|
-
|
16
|
-
# 添加项目根目录到Python路径
|
17
|
-
project_root = Path(__file__).parent.parent.parent
|
18
|
-
sys.path.insert(0, str(project_root))
|
19
|
-
|
20
|
-
from jettask.webui_config import PostgreSQLConfig
|
21
|
-
|
22
|
-
|
23
|
-
def run_fastapi_server(host: str = "0.0.0.0", port: int = 8000, with_consumer: bool = False):
|
24
|
-
"""运行FastAPI服务器"""
|
25
|
-
import uvicorn
|
26
|
-
from jettask.api import app
|
27
|
-
|
28
|
-
# 如果需要启动消费者
|
29
|
-
if with_consumer:
|
30
|
-
pg_config = PostgreSQLConfig.from_env()
|
31
|
-
if pg_config.dsn:
|
32
|
-
app.state.pg_config = pg_config
|
33
|
-
app.state.enable_consumer = True
|
34
|
-
print("PostgreSQL consumer will be started with the server")
|
35
|
-
else:
|
36
|
-
print("Warning: PostgreSQL configuration not found, consumer disabled")
|
37
|
-
|
38
|
-
print(f"Starting FastAPI server on http://{host}:{port}")
|
39
|
-
print("Access the web interface at http://localhost:8000/")
|
40
|
-
|
41
|
-
uvicorn.run(
|
42
|
-
"jettask.webui.api:app",
|
43
|
-
host=host,
|
44
|
-
port=port,
|
45
|
-
reload=False,
|
46
|
-
log_level="info"
|
47
|
-
)
|
48
|
-
|
49
|
-
|
50
|
-
def run_gradio_interface(host: str = "0.0.0.0", port: int = 7860, share: bool = False):
|
51
|
-
"""运行Gradio界面"""
|
52
|
-
# 确保FastAPI服务器正在运行
|
53
|
-
print("Note: Make sure the FastAPI server is running on port 8000")
|
54
|
-
print("You can start it with: python -m jettask.webui.run_webui --mode fastapi")
|
55
|
-
print()
|
56
|
-
|
57
|
-
from jettask.gradio_app import create_interface
|
58
|
-
|
59
|
-
app = create_interface()
|
60
|
-
print(f"Starting Gradio interface on http://{host}:{port}")
|
61
|
-
|
62
|
-
app.launch(
|
63
|
-
server_name=host,
|
64
|
-
server_port=port,
|
65
|
-
share=share,
|
66
|
-
inbrowser=True
|
67
|
-
)
|
68
|
-
|
69
|
-
|
70
|
-
def run_combined_mode(host: str = "0.0.0.0", api_port: int = 8000, ui_port: int = 7860):
|
71
|
-
"""同时运行FastAPI和Gradio"""
|
72
|
-
import subprocess
|
73
|
-
import time
|
74
|
-
|
75
|
-
# 启动FastAPI服务器
|
76
|
-
print("Starting FastAPI server...")
|
77
|
-
api_process = subprocess.Popen([
|
78
|
-
sys.executable, "-m", "jettask.webui.api"
|
79
|
-
])
|
80
|
-
|
81
|
-
# 等待API服务器启动
|
82
|
-
time.sleep(3)
|
83
|
-
|
84
|
-
# 启动Gradio界面
|
85
|
-
print("\nStarting Gradio interface...")
|
86
|
-
try:
|
87
|
-
run_gradio_interface(host, ui_port, share=False)
|
88
|
-
except KeyboardInterrupt:
|
89
|
-
print("\nShutting down...")
|
90
|
-
finally:
|
91
|
-
api_process.terminate()
|
92
|
-
api_process.wait()
|
93
|
-
|
94
|
-
|
95
|
-
def run_integrated_gradio(host: str = "0.0.0.0", port: int = 7860, share: bool = False):
|
96
|
-
"""运行集成的Gradio界面(直接访问数据源)"""
|
97
|
-
from jettask.integrated_gradio_app import create_integrated_interface
|
98
|
-
|
99
|
-
print("Starting Integrated Gradio interface (direct data access)...")
|
100
|
-
print(f"Access the interface at http://{host}:{port}")
|
101
|
-
|
102
|
-
app = create_integrated_interface()
|
103
|
-
app.launch(
|
104
|
-
server_name=host,
|
105
|
-
server_port=port,
|
106
|
-
share=share,
|
107
|
-
inbrowser=True
|
108
|
-
)
|
109
|
-
|
110
|
-
|
111
|
-
def main():
|
112
|
-
parser = argparse.ArgumentParser(description="JetTask WebUI 启动器")
|
113
|
-
parser.add_argument(
|
114
|
-
"--mode",
|
115
|
-
choices=["fastapi", "gradio", "combined", "integrated"],
|
116
|
-
default="integrated",
|
117
|
-
help="界面模式: fastapi(原始HTML), gradio(新界面), combined(同时启动), integrated(集成版,直接访问数据)"
|
118
|
-
)
|
119
|
-
parser.add_argument("--host", default="0.0.0.0", help="服务器地址")
|
120
|
-
parser.add_argument("--port", type=int, help="服务器端口")
|
121
|
-
parser.add_argument("--with-consumer", action="store_true", help="启动PostgreSQL消费者")
|
122
|
-
parser.add_argument("--share", action="store_true", help="创建Gradio公共链接")
|
123
|
-
|
124
|
-
args = parser.parse_args()
|
125
|
-
|
126
|
-
# 设置日志
|
127
|
-
logging.basicConfig(
|
128
|
-
level=logging.INFO,
|
129
|
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
130
|
-
)
|
131
|
-
|
132
|
-
if args.mode == "fastapi":
|
133
|
-
port = args.port or 8000
|
134
|
-
run_fastapi_server(args.host, port, args.with_consumer)
|
135
|
-
elif args.mode == "gradio":
|
136
|
-
port = args.port or 7860
|
137
|
-
run_gradio_interface(args.host, port, args.share)
|
138
|
-
elif args.mode == "combined":
|
139
|
-
run_combined_mode(args.host)
|
140
|
-
elif args.mode == "integrated":
|
141
|
-
port = args.port or 7860
|
142
|
-
run_integrated_gradio(args.host, port, args.share)
|
143
|
-
else:
|
144
|
-
parser.print_help()
|
145
|
-
|
146
|
-
|
147
|
-
if __name__ == "__main__":
|
148
|
-
main()
|
@@ -1,294 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
多命名空间调度器管理器
|
3
|
-
自动检测和管理多个命名空间的调度器实例
|
4
|
-
"""
|
5
|
-
import asyncio
|
6
|
-
import logging
|
7
|
-
import multiprocessing
|
8
|
-
import traceback
|
9
|
-
from typing import Dict, Set
|
10
|
-
|
11
|
-
from jettask import Jettask
|
12
|
-
from jettask.task_center import TaskCenter
|
13
|
-
from jettask.scheduler.scheduler import TaskScheduler
|
14
|
-
from jettask.scheduler.manager import ScheduledTaskManager
|
15
|
-
|
16
|
-
logger = logging.getLogger(__name__)
|
17
|
-
|
18
|
-
|
19
|
-
class MultiNamespaceSchedulerManager:
|
20
|
-
"""多命名空间调度器管理器"""
|
21
|
-
|
22
|
-
def __init__(self,
|
23
|
-
task_center_base_url: str,
|
24
|
-
check_interval: int = 30,
|
25
|
-
scheduler_interval: float = 0.1,
|
26
|
-
batch_size: int = 100,
|
27
|
-
debug: bool = False):
|
28
|
-
"""
|
29
|
-
初始化多命名空间调度器管理器
|
30
|
-
|
31
|
-
Args:
|
32
|
-
task_center_base_url: 任务中心基础URL (如 http://localhost:8001)
|
33
|
-
check_interval: 命名空间检测间隔(秒)
|
34
|
-
scheduler_interval: 调度器扫描间隔(秒)
|
35
|
-
batch_size: 每批处理的最大任务数
|
36
|
-
debug: 是否启用调试模式
|
37
|
-
"""
|
38
|
-
self.task_center_base_url = task_center_base_url.rstrip('/')
|
39
|
-
self.check_interval = check_interval
|
40
|
-
self.scheduler_interval = scheduler_interval
|
41
|
-
self.batch_size = batch_size
|
42
|
-
self.debug = debug
|
43
|
-
|
44
|
-
# 存储每个命名空间的进程
|
45
|
-
self.scheduler_processes: Dict[str, multiprocessing.Process] = {}
|
46
|
-
self.running = False
|
47
|
-
|
48
|
-
# 设置日志级别
|
49
|
-
if debug:
|
50
|
-
logging.basicConfig(level=logging.DEBUG)
|
51
|
-
else:
|
52
|
-
logging.basicConfig(level=logging.INFO)
|
53
|
-
|
54
|
-
async def get_active_namespaces(self) -> Set[str]:
|
55
|
-
"""获取所有活跃的命名空间"""
|
56
|
-
import aiohttp
|
57
|
-
|
58
|
-
try:
|
59
|
-
# 直接调用API获取命名空间列表
|
60
|
-
url = f"{self.task_center_base_url}/api/namespaces"
|
61
|
-
|
62
|
-
async with aiohttp.ClientSession() as session:
|
63
|
-
async with session.get(url) as response:
|
64
|
-
if response.status == 200:
|
65
|
-
namespaces = await response.json()
|
66
|
-
if namespaces:
|
67
|
-
active_names = {ns['name'] for ns in namespaces if ns.get('name')}
|
68
|
-
logger.info(f"发现 {len(active_names)} 个活跃的命名空间: {active_names}")
|
69
|
-
return active_names
|
70
|
-
else:
|
71
|
-
logger.warning("没有找到任何命名空间")
|
72
|
-
return set()
|
73
|
-
else:
|
74
|
-
logger.error(f"获取命名空间列表失败,状态码: {response.status}")
|
75
|
-
return set()
|
76
|
-
|
77
|
-
except Exception as e:
|
78
|
-
logger.error(f"获取命名空间列表失败: {e}")
|
79
|
-
return set()
|
80
|
-
|
81
|
-
def start_scheduler_for_namespace(self, namespace: str):
|
82
|
-
"""为指定命名空间启动调度器进程"""
|
83
|
-
if namespace in self.scheduler_processes:
|
84
|
-
# 检查进程是否还在运行
|
85
|
-
if self.scheduler_processes[namespace].is_alive():
|
86
|
-
return
|
87
|
-
else:
|
88
|
-
# 清理已停止的进程
|
89
|
-
logger.info(f"清理已停止的调度器进程: {namespace}")
|
90
|
-
self.scheduler_processes[namespace].terminate()
|
91
|
-
self.scheduler_processes[namespace].join(timeout=5)
|
92
|
-
del self.scheduler_processes[namespace]
|
93
|
-
|
94
|
-
# 创建新进程
|
95
|
-
process = multiprocessing.Process(
|
96
|
-
target=run_scheduler_for_namespace,
|
97
|
-
args=(
|
98
|
-
namespace,
|
99
|
-
self.task_center_base_url,
|
100
|
-
self.scheduler_interval,
|
101
|
-
self.batch_size,
|
102
|
-
self.debug
|
103
|
-
),
|
104
|
-
name=f"scheduler_{namespace}"
|
105
|
-
)
|
106
|
-
|
107
|
-
process.start()
|
108
|
-
self.scheduler_processes[namespace] = process
|
109
|
-
logger.info(f"启动命名空间 {namespace} 的调度器进程, PID: {process.pid}")
|
110
|
-
|
111
|
-
def stop_scheduler_for_namespace(self, namespace: str):
|
112
|
-
"""停止指定命名空间的调度器进程"""
|
113
|
-
if namespace in self.scheduler_processes:
|
114
|
-
process = self.scheduler_processes[namespace]
|
115
|
-
if process.is_alive():
|
116
|
-
logger.info(f"停止命名空间 {namespace} 的调度器进程")
|
117
|
-
process.terminate()
|
118
|
-
process.join(timeout=10)
|
119
|
-
|
120
|
-
if process.is_alive():
|
121
|
-
logger.warning(f"强制停止命名空间 {namespace} 的调度器进程")
|
122
|
-
process.kill()
|
123
|
-
process.join(timeout=5)
|
124
|
-
|
125
|
-
del self.scheduler_processes[namespace]
|
126
|
-
|
127
|
-
async def check_and_update_schedulers(self):
|
128
|
-
"""检查并更新调度器(添加新的,停止已删除的)"""
|
129
|
-
# 获取当前活跃的命名空间
|
130
|
-
active_namespaces = await self.get_active_namespaces()
|
131
|
-
current_namespaces = set(self.scheduler_processes.keys())
|
132
|
-
|
133
|
-
# 找出需要添加的命名空间
|
134
|
-
to_add = active_namespaces - current_namespaces
|
135
|
-
# 找出需要删除的命名空间
|
136
|
-
to_remove = current_namespaces - active_namespaces
|
137
|
-
|
138
|
-
# 启动新的调度器
|
139
|
-
for namespace in to_add:
|
140
|
-
logger.info(f"检测到新命名空间: {namespace}")
|
141
|
-
self.start_scheduler_for_namespace(namespace)
|
142
|
-
|
143
|
-
# 停止已删除的调度器
|
144
|
-
for namespace in to_remove:
|
145
|
-
logger.info(f"检测到命名空间已删除: {namespace}")
|
146
|
-
self.stop_scheduler_for_namespace(namespace)
|
147
|
-
|
148
|
-
# 检查现有进程的健康状态
|
149
|
-
for namespace in active_namespaces & current_namespaces:
|
150
|
-
process = self.scheduler_processes.get(namespace)
|
151
|
-
if process and not process.is_alive():
|
152
|
-
logger.warning(f"调度器进程 {namespace} 已停止,重新启动")
|
153
|
-
del self.scheduler_processes[namespace]
|
154
|
-
self.start_scheduler_for_namespace(namespace)
|
155
|
-
|
156
|
-
async def run(self):
|
157
|
-
"""运行多命名空间调度器管理器"""
|
158
|
-
self.running = True
|
159
|
-
logger.info(f"启动多命名空间调度器管理器")
|
160
|
-
logger.info(f"任务中心: {self.task_center_base_url}")
|
161
|
-
logger.info(f"命名空间检测间隔: {self.check_interval} 秒")
|
162
|
-
logger.info(f"调度器扫描间隔: {self.scheduler_interval} 秒")
|
163
|
-
logger.info(f"批处理大小: {self.batch_size}")
|
164
|
-
|
165
|
-
# 初始检查和启动
|
166
|
-
await self.check_and_update_schedulers()
|
167
|
-
|
168
|
-
# 定期检查命名空间变化
|
169
|
-
while self.running:
|
170
|
-
try:
|
171
|
-
await asyncio.sleep(self.check_interval)
|
172
|
-
await self.check_and_update_schedulers()
|
173
|
-
except asyncio.CancelledError:
|
174
|
-
break
|
175
|
-
except Exception as e:
|
176
|
-
logger.error(f"检查命名空间时出错: {e}")
|
177
|
-
if self.debug:
|
178
|
-
traceback.print_exc()
|
179
|
-
|
180
|
-
def stop(self):
|
181
|
-
"""停止管理器和所有调度器"""
|
182
|
-
logger.info("停止多命名空间调度器管理器")
|
183
|
-
self.running = False
|
184
|
-
|
185
|
-
# 停止所有调度器进程
|
186
|
-
for namespace in list(self.scheduler_processes.keys()):
|
187
|
-
self.stop_scheduler_for_namespace(namespace)
|
188
|
-
|
189
|
-
logger.info("所有调度器已停止")
|
190
|
-
|
191
|
-
|
192
|
-
def run_scheduler_for_namespace(namespace: str,
|
193
|
-
task_center_base_url: str,
|
194
|
-
interval: float,
|
195
|
-
batch_size: int,
|
196
|
-
debug: bool):
|
197
|
-
"""在独立进程中运行指定命名空间的调度器"""
|
198
|
-
import asyncio
|
199
|
-
import logging
|
200
|
-
import signal
|
201
|
-
import sys
|
202
|
-
|
203
|
-
# 设置进程标题(如果可用)
|
204
|
-
try:
|
205
|
-
import setproctitle # type: ignore
|
206
|
-
setproctitle.setproctitle(f"jettask-scheduler-{namespace}")
|
207
|
-
except ImportError:
|
208
|
-
pass
|
209
|
-
|
210
|
-
# 配置日志
|
211
|
-
logging.basicConfig(
|
212
|
-
level=logging.DEBUG if debug else logging.INFO,
|
213
|
-
format=f'%(asctime)s - %(levelname)s - [{namespace}] %(message)s'
|
214
|
-
)
|
215
|
-
logger = logging.getLogger(__name__)
|
216
|
-
|
217
|
-
async def run_scheduler():
|
218
|
-
"""运行调度器的异步函数"""
|
219
|
-
scheduler_instance = None
|
220
|
-
try:
|
221
|
-
# 构建命名空间特定的URL
|
222
|
-
task_center_url = f"{task_center_base_url}/api/v1/namespaces/{namespace}"
|
223
|
-
logger.info(f"连接到任务中心: {task_center_url}")
|
224
|
-
|
225
|
-
# 连接任务中心
|
226
|
-
tc = TaskCenter(task_center_url)
|
227
|
-
if not tc._connect_sync():
|
228
|
-
logger.error(f"无法连接到任务中心: {namespace}")
|
229
|
-
return
|
230
|
-
|
231
|
-
logger.info(f"成功连接到命名空间: {tc.namespace_name}")
|
232
|
-
|
233
|
-
# 创建app实例
|
234
|
-
app = Jettask(task_center=tc)
|
235
|
-
|
236
|
-
if not app.redis_url or not app.pg_url:
|
237
|
-
logger.error(f"任务中心配置不完整: {namespace}")
|
238
|
-
return
|
239
|
-
|
240
|
-
# 显示配置信息
|
241
|
-
logger.info(f"命名空间 {namespace} 的调度器配置:")
|
242
|
-
logger.info(f" Redis: {app.redis_url}")
|
243
|
-
logger.info(f" PostgreSQL: {app.pg_url}")
|
244
|
-
logger.info(f" 间隔: {interval} 秒")
|
245
|
-
logger.info(f" 批大小: {batch_size}")
|
246
|
-
|
247
|
-
# 创建调度器实例
|
248
|
-
manager = ScheduledTaskManager(app)
|
249
|
-
scheduler_instance = TaskScheduler(
|
250
|
-
app=app,
|
251
|
-
db_manager=manager,
|
252
|
-
scan_interval=interval,
|
253
|
-
batch_size=batch_size
|
254
|
-
)
|
255
|
-
|
256
|
-
# 运行调度器(run方法内部会处理连接)
|
257
|
-
logger.info(f"启动命名空间 {namespace} 的调度器...")
|
258
|
-
await scheduler_instance.run()
|
259
|
-
|
260
|
-
except asyncio.CancelledError:
|
261
|
-
logger.info(f"调度器 {namespace} 收到取消信号")
|
262
|
-
except KeyboardInterrupt:
|
263
|
-
logger.info(f"调度器 {namespace} 收到中断信号")
|
264
|
-
except Exception as e:
|
265
|
-
logger.error(f"调度器 {namespace} 运行错误: {e}")
|
266
|
-
if debug:
|
267
|
-
traceback.print_exc()
|
268
|
-
finally:
|
269
|
-
# 清理资源
|
270
|
-
if scheduler_instance:
|
271
|
-
scheduler_instance.stop()
|
272
|
-
|
273
|
-
logger.info(f"调度器 {namespace} 已停止")
|
274
|
-
|
275
|
-
# 设置信号处理
|
276
|
-
import signal
|
277
|
-
import sys
|
278
|
-
|
279
|
-
def signal_handler(signum, frame):
|
280
|
-
logger.info(f"调度器 {namespace} 收到信号 {signum}")
|
281
|
-
sys.exit(0)
|
282
|
-
|
283
|
-
signal.signal(signal.SIGTERM, signal_handler)
|
284
|
-
signal.signal(signal.SIGINT, signal_handler)
|
285
|
-
|
286
|
-
# 运行调度器
|
287
|
-
try:
|
288
|
-
asyncio.run(run_scheduler())
|
289
|
-
except (KeyboardInterrupt, SystemExit):
|
290
|
-
logger.info(f"调度器 {namespace} 正常退出")
|
291
|
-
except Exception as e:
|
292
|
-
logger.error(f"调度器 {namespace} 异常退出: {e}")
|
293
|
-
if debug:
|
294
|
-
traceback.print_exc()
|