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.
Files changed (165) hide show
  1. jettask/__init__.py +10 -3
  2. jettask/cli.py +314 -228
  3. jettask/config/__init__.py +9 -1
  4. jettask/config/config.py +245 -0
  5. jettask/config/env_loader.py +381 -0
  6. jettask/config/lua_scripts.py +158 -0
  7. jettask/config/nacos_config.py +132 -5
  8. jettask/core/__init__.py +1 -1
  9. jettask/core/app.py +1573 -666
  10. jettask/core/app_importer.py +33 -16
  11. jettask/core/container.py +532 -0
  12. jettask/core/task.py +1 -4
  13. jettask/core/unified_manager_base.py +2 -2
  14. jettask/executor/__init__.py +38 -0
  15. jettask/executor/core.py +625 -0
  16. jettask/executor/executor.py +338 -0
  17. jettask/executor/orchestrator.py +290 -0
  18. jettask/executor/process_entry.py +638 -0
  19. jettask/executor/task_executor.py +317 -0
  20. jettask/messaging/__init__.py +68 -0
  21. jettask/messaging/event_pool.py +2188 -0
  22. jettask/messaging/reader.py +519 -0
  23. jettask/messaging/registry.py +266 -0
  24. jettask/messaging/scanner.py +369 -0
  25. jettask/messaging/sender.py +312 -0
  26. jettask/persistence/__init__.py +118 -0
  27. jettask/persistence/backlog_monitor.py +567 -0
  28. jettask/{backend/data_access.py → persistence/base.py} +58 -57
  29. jettask/persistence/consumer.py +315 -0
  30. jettask/{core → persistence}/db_manager.py +23 -22
  31. jettask/persistence/maintenance.py +81 -0
  32. jettask/persistence/message_consumer.py +259 -0
  33. jettask/{backend/namespace_data_access.py → persistence/namespace.py} +66 -98
  34. jettask/persistence/offline_recovery.py +196 -0
  35. jettask/persistence/queue_discovery.py +215 -0
  36. jettask/persistence/task_persistence.py +218 -0
  37. jettask/persistence/task_updater.py +583 -0
  38. jettask/scheduler/__init__.py +2 -2
  39. jettask/scheduler/loader.py +6 -5
  40. jettask/scheduler/run_scheduler.py +1 -1
  41. jettask/scheduler/scheduler.py +7 -7
  42. jettask/scheduler/{unified_scheduler_manager.py → scheduler_coordinator.py} +18 -13
  43. jettask/task/__init__.py +16 -0
  44. jettask/{router.py → task/router.py} +26 -8
  45. jettask/task/task_center/__init__.py +9 -0
  46. jettask/task/task_executor.py +318 -0
  47. jettask/task/task_registry.py +291 -0
  48. jettask/test_connection_monitor.py +73 -0
  49. jettask/utils/__init__.py +31 -1
  50. jettask/{monitor/run_backlog_collector.py → utils/backlog_collector.py} +1 -1
  51. jettask/utils/db_connector.py +1629 -0
  52. jettask/{db_init.py → utils/db_init.py} +1 -1
  53. jettask/utils/rate_limit/__init__.py +30 -0
  54. jettask/utils/rate_limit/concurrency_limiter.py +665 -0
  55. jettask/utils/rate_limit/config.py +145 -0
  56. jettask/utils/rate_limit/limiter.py +41 -0
  57. jettask/utils/rate_limit/manager.py +269 -0
  58. jettask/utils/rate_limit/qps_limiter.py +154 -0
  59. jettask/utils/rate_limit/task_limiter.py +384 -0
  60. jettask/utils/serializer.py +3 -0
  61. jettask/{monitor/stream_backlog_monitor.py → utils/stream_backlog.py} +14 -6
  62. jettask/utils/time_sync.py +173 -0
  63. jettask/webui/__init__.py +27 -0
  64. jettask/{api/v1 → webui/api}/alerts.py +1 -1
  65. jettask/{api/v1 → webui/api}/analytics.py +2 -2
  66. jettask/{api/v1 → webui/api}/namespaces.py +1 -1
  67. jettask/{api/v1 → webui/api}/overview.py +1 -1
  68. jettask/{api/v1 → webui/api}/queues.py +3 -3
  69. jettask/{api/v1 → webui/api}/scheduled.py +1 -1
  70. jettask/{api/v1 → webui/api}/settings.py +1 -1
  71. jettask/{api.py → webui/app.py} +253 -145
  72. jettask/webui/namespace_manager/__init__.py +10 -0
  73. jettask/{multi_namespace_consumer.py → webui/namespace_manager/multi.py} +69 -22
  74. jettask/{unified_consumer_manager.py → webui/namespace_manager/unified.py} +1 -1
  75. jettask/{run.py → webui/run.py} +2 -2
  76. jettask/{services → webui/services}/__init__.py +1 -3
  77. jettask/{services → webui/services}/overview_service.py +34 -16
  78. jettask/{services → webui/services}/queue_service.py +1 -1
  79. jettask/{backend → webui/services}/queue_stats_v2.py +1 -1
  80. jettask/{services → webui/services}/settings_service.py +1 -1
  81. jettask/worker/__init__.py +53 -0
  82. jettask/worker/lifecycle.py +1507 -0
  83. jettask/worker/manager.py +583 -0
  84. jettask/{core/offline_worker_recovery.py → worker/recovery.py} +268 -175
  85. {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/METADATA +2 -71
  86. jettask-0.2.20.dist-info/RECORD +145 -0
  87. jettask/__main__.py +0 -140
  88. jettask/api/__init__.py +0 -103
  89. jettask/backend/__init__.py +0 -1
  90. jettask/backend/api/__init__.py +0 -3
  91. jettask/backend/api/v1/__init__.py +0 -17
  92. jettask/backend/api/v1/monitoring.py +0 -431
  93. jettask/backend/api/v1/namespaces.py +0 -504
  94. jettask/backend/api/v1/queues.py +0 -342
  95. jettask/backend/api/v1/tasks.py +0 -367
  96. jettask/backend/core/__init__.py +0 -3
  97. jettask/backend/core/cache.py +0 -221
  98. jettask/backend/core/database.py +0 -200
  99. jettask/backend/core/exceptions.py +0 -102
  100. jettask/backend/dependencies.py +0 -261
  101. jettask/backend/init_meta_db.py +0 -158
  102. jettask/backend/main.py +0 -1426
  103. jettask/backend/main_unified.py +0 -78
  104. jettask/backend/main_v2.py +0 -394
  105. jettask/backend/models/__init__.py +0 -3
  106. jettask/backend/models/requests.py +0 -236
  107. jettask/backend/models/responses.py +0 -230
  108. jettask/backend/namespace_api_old.py +0 -267
  109. jettask/backend/services/__init__.py +0 -3
  110. jettask/backend/start.py +0 -42
  111. jettask/backend/unified_api_router.py +0 -1541
  112. jettask/cleanup_deprecated_tables.sql +0 -16
  113. jettask/core/consumer_manager.py +0 -1695
  114. jettask/core/delay_scanner.py +0 -256
  115. jettask/core/event_pool.py +0 -1700
  116. jettask/core/heartbeat_process.py +0 -222
  117. jettask/core/task_batch.py +0 -153
  118. jettask/core/worker_scanner.py +0 -271
  119. jettask/executors/__init__.py +0 -5
  120. jettask/executors/asyncio.py +0 -876
  121. jettask/executors/base.py +0 -30
  122. jettask/executors/common.py +0 -148
  123. jettask/executors/multi_asyncio.py +0 -309
  124. jettask/gradio_app.py +0 -570
  125. jettask/integrated_gradio_app.py +0 -1088
  126. jettask/main.py +0 -0
  127. jettask/monitoring/__init__.py +0 -3
  128. jettask/pg_consumer.py +0 -1896
  129. jettask/run_monitor.py +0 -22
  130. jettask/run_webui.py +0 -148
  131. jettask/scheduler/multi_namespace_scheduler.py +0 -294
  132. jettask/scheduler/unified_manager.py +0 -450
  133. jettask/task_center_client.py +0 -150
  134. jettask/utils/serializer_optimized.py +0 -33
  135. jettask/webui_exceptions.py +0 -67
  136. jettask-0.2.19.dist-info/RECORD +0 -150
  137. /jettask/{constants.py → config/constants.py} +0 -0
  138. /jettask/{backend/config.py → config/task_center.py} +0 -0
  139. /jettask/{pg_consumer → messaging/pg_consumer}/pg_consumer_v2.py +0 -0
  140. /jettask/{pg_consumer → messaging/pg_consumer}/sql/add_execution_time_field.sql +0 -0
  141. /jettask/{pg_consumer → messaging/pg_consumer}/sql/create_new_tables.sql +0 -0
  142. /jettask/{pg_consumer → messaging/pg_consumer}/sql/create_tables_v3.sql +0 -0
  143. /jettask/{pg_consumer → messaging/pg_consumer}/sql/migrate_to_new_structure.sql +0 -0
  144. /jettask/{pg_consumer → messaging/pg_consumer}/sql/modify_time_fields.sql +0 -0
  145. /jettask/{pg_consumer → messaging/pg_consumer}/sql_utils.py +0 -0
  146. /jettask/{models.py → persistence/models.py} +0 -0
  147. /jettask/scheduler/{manager.py → task_crud.py} +0 -0
  148. /jettask/{schema.sql → schemas/schema.sql} +0 -0
  149. /jettask/{task_center.py → task/task_center/client.py} +0 -0
  150. /jettask/{monitoring → utils}/file_watcher.py +0 -0
  151. /jettask/{services/redis_monitor_service.py → utils/redis_monitor.py} +0 -0
  152. /jettask/{api/v1 → webui/api}/__init__.py +0 -0
  153. /jettask/{webui_config.py → webui/config.py} +0 -0
  154. /jettask/{webui_models → webui/models}/__init__.py +0 -0
  155. /jettask/{webui_models → webui/models}/namespace.py +0 -0
  156. /jettask/{services → webui/services}/alert_service.py +0 -0
  157. /jettask/{services → webui/services}/analytics_service.py +0 -0
  158. /jettask/{services → webui/services}/scheduled_task_service.py +0 -0
  159. /jettask/{services → webui/services}/task_service.py +0 -0
  160. /jettask/{webui_sql → webui/sql}/batch_upsert_functions.sql +0 -0
  161. /jettask/{webui_sql → webui/sql}/verify_database.sql +0 -0
  162. {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/WHEEL +0 -0
  163. {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/entry_points.txt +0 -0
  164. {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/licenses/LICENSE +0 -0
  165. {jettask-0.2.19.dist-info → jettask-0.2.20.dist-info}/top_level.txt +0 -0
@@ -29,6 +29,8 @@ class AppImporter:
29
29
  - "module" - 从 module 自动查找应用实例
30
30
  - "path/to/file.py:app" - 从文件路径导入
31
31
  - "path/to/file.py" - 从文件自动查找
32
+ - "path/to/dir:app" - 从目录的 __init__.py 导入
33
+ - "path/to/dir" - 从目录的 __init__.py 自动查找
32
34
  """
33
35
  # 分离模块路径和应用名称
34
36
  if ':' in import_str:
@@ -37,8 +39,8 @@ class AppImporter:
37
39
  module_str = import_str
38
40
  app_name = None
39
41
 
40
- # 判断是文件路径还是模块名
41
- if '/' in module_str or module_str.endswith('.py'):
42
+ # 判断是文件路径、目录路径还是模块名
43
+ if '/' in module_str or module_str.endswith('.py') or os.path.isdir(module_str):
42
44
  module = cls._import_from_file(module_str)
43
45
  else:
44
46
  module = cls._import_from_module(module_str)
@@ -63,28 +65,43 @@ class AppImporter:
63
65
 
64
66
  @classmethod
65
67
  def _import_from_file(cls, file_path: str):
66
- """从文件路径导入模块"""
68
+ """从文件路径或目录导入模块"""
67
69
  path = Path(file_path)
68
70
 
69
71
  # 处理相对路径
70
72
  if not path.is_absolute():
71
73
  path = Path.cwd() / path
72
74
 
73
- # 去掉 .py 后缀(如果有)
74
- if path.suffix == '.py':
75
- path = path.with_suffix('')
76
-
77
- # 检查文件是否存在
78
- py_file = path.with_suffix('.py')
79
- if not py_file.exists():
80
- raise ImportError(f"File not found: {py_file}")
75
+ # 判断是目录还是文件
76
+ if path.is_dir():
77
+ # 目录:查找 __init__.py
78
+ init_file = path / '__init__.py'
79
+ if not init_file.exists():
80
+ raise ImportError(
81
+ f"Directory {path} does not contain __init__.py. "
82
+ f"Cannot import as a Python package."
83
+ )
84
+ py_file = init_file
85
+ module_name = path.name
86
+ parent_dir = path.parent
87
+ else:
88
+ # 文件:处理 .py 后缀
89
+ if path.suffix == '.py':
90
+ path = path.with_suffix('')
91
+
92
+ # 检查文件是否存在
93
+ py_file = path.with_suffix('.py')
94
+ if not py_file.exists():
95
+ raise ImportError(f"File not found: {py_file}")
96
+
97
+ module_name = path.name
98
+ parent_dir = path.parent
81
99
 
82
- # 添加到 sys.path
83
- sys.path.insert(0, str(path.parent))
100
+ # 添加父目录到 sys.path
101
+ sys.path.insert(0, str(parent_dir))
84
102
 
85
103
  try:
86
104
  # 导入模块
87
- module_name = path.name
88
105
  spec = importlib.util.spec_from_file_location(
89
106
  module_name,
90
107
  str(py_file)
@@ -113,8 +130,8 @@ class AppImporter:
113
130
  raise ImportError(f"Cannot create module spec for {py_file}")
114
131
  finally:
115
132
  # 清理 sys.path
116
- if str(path.parent) in sys.path:
117
- sys.path.remove(str(path.parent))
133
+ if str(parent_dir) in sys.path:
134
+ sys.path.remove(str(parent_dir))
118
135
 
119
136
  @classmethod
120
137
  def _import_from_module(cls, module_str: str):
@@ -0,0 +1,532 @@
1
+ """
2
+ 依赖注入容器 - 统一管理服务实例
3
+ """
4
+
5
+ import logging
6
+ from typing import Optional, Dict, Any
7
+ import redis
8
+ import redis.asyncio as aioredis
9
+
10
+ from jettask.config.config import JetTaskConfig
11
+
12
+ logger = logging.getLogger('app')
13
+
14
+
15
+ class ServiceContainer:
16
+ """
17
+ 服务容器 - 管理所有服务实例
18
+
19
+ 职责:
20
+ 1. 统一管理 Redis 客户端(避免重复创建)
21
+ 2. 统一管理消息处理组件(MessageSender, MessageReader, DelayedMessageScanner)
22
+ 3. 统一管理 Worker 相关组件(WorkerStateManager, WorkerScanner)
23
+ 4. 提供单例模式,确保全局唯一
24
+
25
+ 设计原则:
26
+ - 延迟初始化:只在需要时创建实例
27
+ - 单例模式:同一配置只创建一次实例
28
+ - 依赖注入:组件之间的依赖由容器管理
29
+ """
30
+
31
+ def __init__(self, config: JetTaskConfig):
32
+ """
33
+ 初始化服务容器
34
+
35
+ Args:
36
+ config: JetTask配置对象
37
+ """
38
+ self.config = config
39
+
40
+ # Redis客户端缓存(从全局客户端获取,不再管理连接池)
41
+ self._sync_text_redis: Optional[redis.Redis] = None
42
+ self._sync_binary_redis: Optional[redis.Redis] = None
43
+ self._async_text_redis: Optional[aioredis.Redis] = None
44
+ self._async_binary_redis: Optional[aioredis.Redis] = None
45
+
46
+ # 消息处理组件
47
+ self._message_sender = None
48
+ self._message_reader = None
49
+ self._delayed_scanner = None
50
+
51
+ # Worker管理组件
52
+ self._worker_state_manager = None
53
+ self._worker_scanner = None
54
+ self._worker_registry = None
55
+
56
+ # 队列和任务注册组件
57
+ self._queue_registry = None
58
+ self._task_registry_with_redis = None
59
+
60
+ # 其他组件
61
+ self._consumer_manager = None
62
+
63
+ # 新架构组件
64
+ self._queue_manager = None
65
+ self._queue_router = None
66
+ self._queue_monitor = None
67
+ self._task_registry = None
68
+ self._task_executor = None
69
+ self._worker_manager = None
70
+
71
+ logger.info(f"ServiceContainer initialized with redis_url={config.redis.url}, prefix={config.redis.prefix}")
72
+
73
+ # ==================== Redis 客户端管理 ====================
74
+
75
+ def _get_sync_text_client(self) -> redis.Redis:
76
+ """获取同步文本模式客户端(使用全局客户端实例)"""
77
+ from jettask.utils.db_connector import get_sync_redis_client
78
+ return get_sync_redis_client(
79
+ redis_url=self.config.redis.url,
80
+ decode_responses=True,
81
+ max_connections=self.config.redis.max_connections
82
+ )
83
+
84
+ def _get_sync_binary_client(self) -> redis.Redis:
85
+ """获取同步二进制模式客户端(使用全局客户端实例)"""
86
+ from jettask.utils.db_connector import get_sync_redis_client
87
+ return get_sync_redis_client(
88
+ redis_url=self.config.redis.url,
89
+ decode_responses=False,
90
+ max_connections=self.config.redis.max_connections
91
+ )
92
+
93
+ def _get_async_text_client(self) -> aioredis.Redis:
94
+ """获取异步文本模式客户端(使用全局客户端实例)"""
95
+ from jettask.utils.db_connector import get_async_redis_client
96
+ return get_async_redis_client(
97
+ redis_url=self.config.redis.url,
98
+ decode_responses=True,
99
+ max_connections=self.config.redis.max_connections
100
+ )
101
+
102
+ def _get_async_binary_client(self) -> aioredis.Redis:
103
+ """获取异步二进制模式客户端(使用全局客户端实例)"""
104
+ from jettask.utils.db_connector import get_async_redis_client
105
+ return get_async_redis_client(
106
+ redis_url=self.config.redis.url,
107
+ decode_responses=False,
108
+ max_connections=self.config.redis.max_connections
109
+ )
110
+
111
+ def get_redis_client(self, async_mode: bool = False, binary: bool = False) -> Any:
112
+ """
113
+ 获取 Redis 客户端(单例模式)
114
+
115
+ Args:
116
+ async_mode: 是否使用异步客户端
117
+ binary: 是否使用二进制模式(用于Stream操作)
118
+
119
+ Returns:
120
+ Redis客户端实例
121
+
122
+ 示例:
123
+ # 同步文本模式(用于普通操作)
124
+ redis = container.get_redis_client(async_mode=False, binary=False)
125
+
126
+ # 异步二进制模式(用于Stream操作)
127
+ redis = container.get_redis_client(async_mode=True, binary=True)
128
+ """
129
+ if async_mode:
130
+ if binary:
131
+ if not self._async_binary_redis:
132
+ self._async_binary_redis = self._get_async_binary_client()
133
+ logger.debug("Obtained async binary Redis client from global cache")
134
+ return self._async_binary_redis
135
+ else:
136
+ if not self._async_text_redis:
137
+ self._async_text_redis = self._get_async_text_client()
138
+ logger.debug("Obtained async text Redis client from global cache")
139
+ return self._async_text_redis
140
+ else:
141
+ if binary:
142
+ if not self._sync_binary_redis:
143
+ self._sync_binary_redis = self._get_sync_binary_client()
144
+ logger.debug("Obtained sync binary Redis client from global cache")
145
+ return self._sync_binary_redis
146
+ else:
147
+ if not self._sync_text_redis:
148
+ self._sync_text_redis = self._get_sync_text_client()
149
+ logger.debug("Obtained sync text Redis client from global cache")
150
+ return self._sync_text_redis
151
+
152
+ # ==================== 消息处理组件 ====================
153
+
154
+ def get_message_sender(self):
155
+ """
156
+ 获取 MessageSender 实例(单例)
157
+
158
+ Returns:
159
+ MessageSender实例
160
+ """
161
+ if not self._message_sender:
162
+ from ..messaging.sender import MessageSender
163
+
164
+ self._message_sender = MessageSender(
165
+ async_redis_client=self.get_redis_client(async_mode=True, binary=True),
166
+ redis_prefix=self.config.redis.prefix
167
+ )
168
+ logger.info("Created MessageSender")
169
+
170
+ return self._message_sender
171
+
172
+ def get_message_reader(self):
173
+ """
174
+ 获取 MessageReader 实例(单例)
175
+
176
+ Returns:
177
+ MessageReader实例
178
+ """
179
+ if not self._message_reader:
180
+ from ..messaging.reader import MessageReader
181
+
182
+ self._message_reader = MessageReader(
183
+ async_redis_client=self.get_redis_client(async_mode=True, binary=False),
184
+ async_binary_redis_client=self.get_redis_client(async_mode=True, binary=True),
185
+ redis_prefix=self.config.redis.prefix
186
+ )
187
+ logger.info("Created MessageReader")
188
+
189
+ return self._message_reader
190
+
191
+ def get_delayed_scanner(self):
192
+ """
193
+ 获取 DelayedMessageScanner 实例(单例)
194
+
195
+ Returns:
196
+ DelayedMessageScanner实例
197
+ """
198
+ if not self._delayed_scanner:
199
+ from ..messaging.scanner import DelayedMessageScanner
200
+
201
+ self._delayed_scanner = DelayedMessageScanner(
202
+ async_binary_redis_client=self.get_redis_client(async_mode=True, binary=True),
203
+ redis_prefix=self.config.redis.prefix,
204
+ scan_interval=self.config.message.delayed_scan_interval,
205
+ batch_size=self.config.message.delayed_batch_size
206
+ )
207
+ logger.info("Created DelayedMessageScanner")
208
+
209
+ return self._delayed_scanner
210
+
211
+ # ==================== Worker 管理组件 ====================
212
+
213
+ def get_worker_state_manager(self):
214
+ """
215
+ 获取 WorkerStateManager 实例(单例)
216
+
217
+ Returns:
218
+ WorkerStateManager实例
219
+ """
220
+ if not self._worker_state_manager:
221
+ from .worker_state_manager import WorkerStateManager
222
+
223
+ self._worker_state_manager = WorkerStateManager(
224
+ redis_client=self.get_redis_client(async_mode=True, binary=False),
225
+ redis_prefix=self.config.redis.prefix
226
+ )
227
+ logger.info("Created WorkerStateManager")
228
+
229
+ return self._worker_state_manager
230
+
231
+ def get_worker_scanner(self):
232
+ """
233
+ 获取 WorkerScanner 实例(单例)
234
+
235
+ Returns:
236
+ WorkerScanner实例
237
+ """
238
+ if not self._worker_scanner:
239
+ from jettask.worker.lifecycle import WorkerScanner
240
+
241
+ self._worker_scanner = WorkerScanner(
242
+ sync_redis=self.get_redis_client(async_mode=False, binary=False),
243
+ async_redis=self.get_redis_client(async_mode=True, binary=False),
244
+ redis_prefix=self.config.redis.prefix,
245
+ heartbeat_timeout=self.config.executor.worker_heartbeat_timeout,
246
+ worker_state_manager=self.get_worker_state_manager()
247
+ )
248
+ logger.info("Created WorkerScanner")
249
+
250
+ return self._worker_scanner
251
+
252
+ # ==================== 注册管理组件 ====================
253
+
254
+ def get_worker_registry(self):
255
+ """
256
+ 获取 WorkerRegistry 实例(单例)
257
+
258
+ Returns:
259
+ WorkerState实例(之前叫 WorkerRegistry)
260
+ """
261
+ if not self._worker_registry:
262
+ from jettask.worker.manager import WorkerState
263
+
264
+ self._worker_registry = WorkerState(
265
+ redis_client=self.get_redis_client(async_mode=False, binary=False),
266
+ async_redis_client=self.get_redis_client(async_mode=True, binary=False),
267
+ redis_prefix=self.config.redis.prefix
268
+ )
269
+ logger.info("Created WorkerState")
270
+
271
+ return self._worker_registry
272
+
273
+ def get_queue_registry(self):
274
+ """
275
+ 获取 QueueRegistry 实例(单例)
276
+
277
+ Returns:
278
+ QueueRegistry实例
279
+ """
280
+ if not self._queue_registry:
281
+ from jettask.messaging.registry import QueueRegistry
282
+
283
+ self._queue_registry = QueueRegistry(
284
+ redis_client=self.get_redis_client(async_mode=False, binary=False),
285
+ async_redis_client=self.get_redis_client(async_mode=True, binary=False),
286
+ redis_prefix=self.config.redis.prefix
287
+ )
288
+ logger.info("Created QueueRegistry")
289
+
290
+ return self._queue_registry
291
+
292
+ def get_task_registry_with_redis(self):
293
+ """
294
+ 获取带 Redis 功能的 TaskRegistry 实例(单例)
295
+
296
+ Returns:
297
+ TaskRegistry实例
298
+ """
299
+ if not self._task_registry_with_redis:
300
+ from jettask.task.task_registry import TaskRegistry
301
+
302
+ self._task_registry_with_redis = TaskRegistry(
303
+ redis_client=self.get_redis_client(async_mode=False, binary=False),
304
+ async_redis_client=self.get_redis_client(async_mode=True, binary=False),
305
+ redis_prefix=self.config.redis.prefix
306
+ )
307
+ logger.info("Created TaskRegistry with Redis")
308
+
309
+ return self._task_registry_with_redis
310
+
311
+ # ==================== 其他组件 ====================
312
+
313
+ def get_consumer_manager(self, queues=None, app=None):
314
+ """
315
+ 获取 ConsumerManager 实例(单例)
316
+
317
+ Args:
318
+ queues: 队列列表(可选,首次调用时需要)
319
+ app: Jettask应用实例(可选)
320
+
321
+ Returns:
322
+ ConsumerManager实例
323
+ """
324
+ if not self._consumer_manager:
325
+ from .consumer_manager import ConsumerManager, ConsumerStrategy
326
+
327
+ if queues is None:
328
+ queues = []
329
+
330
+ strategy = ConsumerStrategy(self.config.consumer.strategy)
331
+
332
+ consumer_config = {
333
+ 'queues': queues,
334
+ 'redis_prefix': self.config.redis.prefix,
335
+ 'redis_url': self.config.redis.url,
336
+ 'heartbeat_interval': self.config.consumer.heartbeat_interval,
337
+ 'heartbeat_timeout': self.config.consumer.heartbeat_timeout,
338
+ 'reuse_timeout': self.config.consumer.reuse_timeout
339
+ }
340
+
341
+ self._consumer_manager = ConsumerManager(
342
+ redis_client=self.get_redis_client(async_mode=False, binary=False),
343
+ strategy=strategy,
344
+ config=consumer_config,
345
+ app=app
346
+ )
347
+ logger.info(f"Created ConsumerManager with strategy={strategy}")
348
+
349
+ return self._consumer_manager
350
+
351
+ # ==================== 新架构组件 ====================
352
+
353
+ def get_queue_manager(self):
354
+ """
355
+ 获取 QueueManager 实例(单例)
356
+
357
+ Returns:
358
+ QueueManager实例
359
+ """
360
+ if not self._queue_manager:
361
+ from jettask.messaging import QueueManager
362
+
363
+ self._queue_manager = QueueManager(
364
+ message_sender=self.get_message_sender(),
365
+ message_reader=self.get_message_reader(),
366
+ redis_client=self.get_redis_client(async_mode=True, binary=False),
367
+ redis_prefix=self.config.redis.prefix
368
+ )
369
+ logger.info("Created QueueManager")
370
+
371
+ return self._queue_manager
372
+
373
+ def get_queue_router(self):
374
+ """
375
+ 获取 QueueRouter 实例(单例)
376
+
377
+ Returns:
378
+ QueueRouter实例
379
+ """
380
+ if not self._queue_router:
381
+ from jettask.messaging import QueueRouter
382
+
383
+ self._queue_router = QueueRouter()
384
+ logger.info("Created QueueRouter")
385
+
386
+ return self._queue_router
387
+
388
+ def get_queue_monitor(self):
389
+ """
390
+ 获取 QueueMonitor 实例(单例)
391
+
392
+ Returns:
393
+ QueueMonitor实例
394
+ """
395
+ if not self._queue_monitor:
396
+ from jettask.messaging import QueueMonitor
397
+
398
+ self._queue_monitor = QueueMonitor(
399
+ queue_manager=self.get_queue_manager()
400
+ )
401
+ logger.info("Created QueueMonitor")
402
+
403
+ return self._queue_monitor
404
+
405
+ def get_task_registry(self):
406
+ """
407
+ 获取 TaskRegistry 实例(单例)
408
+
409
+ Returns:
410
+ TaskRegistry实例
411
+ """
412
+ if not self._task_registry:
413
+ from ..task import TaskRegistry
414
+
415
+ self._task_registry = TaskRegistry()
416
+ logger.info("Created TaskRegistry")
417
+
418
+ return self._task_registry
419
+
420
+ def get_task_executor(self, data_access=None, retry_manager=None):
421
+ """
422
+ 获取 TaskExecutor 实例(单例)
423
+
424
+ Args:
425
+ data_access: 数据访问层(可选)
426
+ retry_manager: 重试管理器(可选)
427
+
428
+ Returns:
429
+ TaskExecutor实例
430
+ """
431
+ if not self._task_executor:
432
+ from ..task import TaskExecutor
433
+
434
+ self._task_executor = TaskExecutor(
435
+ task_registry=self.get_task_registry(),
436
+ data_access=data_access,
437
+ retry_manager=retry_manager
438
+ )
439
+ logger.info("Created TaskExecutor")
440
+
441
+ return self._task_executor
442
+
443
+
444
+ def get_worker_manager(self, strategy=None, worker_name=None):
445
+ """
446
+ 获取 WorkerManager 实例(单例)
447
+
448
+ Args:
449
+ strategy: Worker策略(可选)
450
+ worker_name: Worker名称(FIXED策略时使用)
451
+
452
+ Returns:
453
+ WorkerManager实例
454
+ """
455
+ if not self._worker_manager:
456
+ from ..worker import WorkerManager, WorkerStrategy
457
+
458
+ # 使用配置中的策略,如果没有则默认DYNAMIC
459
+ if strategy is None:
460
+ strategy_str = self.config.consumer.strategy
461
+ if strategy_str == "fixed":
462
+ strategy = WorkerStrategy.FIXED
463
+ elif strategy_str == "pod":
464
+ strategy = WorkerStrategy.POD
465
+ else:
466
+ strategy = WorkerStrategy.DYNAMIC
467
+
468
+ self._worker_manager = WorkerManager(
469
+ redis_client=self.get_redis_client(async_mode=True, binary=False),
470
+ redis_prefix=self.config.redis.prefix,
471
+ strategy=strategy,
472
+ worker_name=worker_name,
473
+ heartbeat_interval=self.config.consumer.heartbeat_interval,
474
+ heartbeat_timeout=self.config.consumer.heartbeat_timeout
475
+ )
476
+ logger.info(f"Created WorkerManager with strategy={strategy}")
477
+
478
+ return self._worker_manager
479
+
480
+ # ==================== 清理资源 ====================
481
+
482
+ async def cleanup(self):
483
+ """
484
+ 清理所有资源(异步)
485
+
486
+ 应在应用关闭时调用
487
+ """
488
+ logger.info("Cleaning up ServiceContainer resources...")
489
+
490
+ # 停止延迟扫描器
491
+ if self._delayed_scanner:
492
+ await self._delayed_scanner.stop()
493
+ logger.debug("Stopped DelayedMessageScanner")
494
+
495
+ # 停止 WorkerStateManager
496
+ if self._worker_state_manager:
497
+ await self._worker_state_manager.stop_listener()
498
+ logger.debug("Stopped WorkerStateManager")
499
+
500
+ # 关闭异步Redis连接
501
+ if self._async_text_redis:
502
+ await self._async_text_redis.aclose()
503
+ logger.debug("Closed async text Redis")
504
+
505
+ if self._async_binary_redis:
506
+ await self._async_binary_redis.aclose()
507
+ logger.debug("Closed async binary Redis")
508
+
509
+ # 关闭同步Redis连接
510
+ if self._sync_text_redis:
511
+ self._sync_text_redis.close()
512
+ logger.debug("Closed sync text Redis")
513
+
514
+ if self._sync_binary_redis:
515
+ self._sync_binary_redis.close()
516
+ logger.debug("Closed sync binary Redis")
517
+
518
+ logger.info("ServiceContainer cleanup completed")
519
+
520
+ def __del__(self):
521
+ """析构函数:确保同步资源被释放"""
522
+ if self._sync_text_redis:
523
+ try:
524
+ self._sync_text_redis.close()
525
+ except:
526
+ pass
527
+
528
+ if self._sync_binary_redis:
529
+ try:
530
+ self._sync_binary_redis.close()
531
+ except:
532
+ pass
jettask/core/task.py CHANGED
@@ -1,7 +1,5 @@
1
1
  from ..utils.serializer import dumps_str, loads_str
2
- import time
3
2
  import inspect
4
- import asyncio
5
3
  from dataclasses import dataclass
6
4
  from typing import Any, Optional, TYPE_CHECKING, get_type_hints, Union
7
5
 
@@ -9,7 +7,6 @@ if TYPE_CHECKING:
9
7
  from .app import Jettask
10
8
 
11
9
  from .context import TaskContext
12
- from .enums import TaskStatus
13
10
 
14
11
 
15
12
  @dataclass
@@ -148,4 +145,4 @@ class Task:
148
145
 
149
146
  async def _get_pending(self, queue: str):
150
147
  return await self._app.ep.read_pending(queue, queue, asyncio=True)
151
-
148
+
@@ -150,7 +150,7 @@ class UnifiedManagerBase(ABC):
150
150
  pg_url = data.get('pg_url', '')
151
151
 
152
152
  if not redis_url or not pg_url:
153
- logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
153
+ # logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
154
154
  continue
155
155
 
156
156
  redis_config = {'url': redis_url}
@@ -185,7 +185,7 @@ class UnifiedManagerBase(ABC):
185
185
  pg_url = data.get('pg_url', '')
186
186
 
187
187
  if not redis_url or not pg_url:
188
- logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
188
+ # logger.warning(f"跳过命名空间 {data['name']}:缺少 Redis 或 PostgreSQL 配置")
189
189
  continue
190
190
 
191
191
  redis_config = {'url': redis_url}
@@ -0,0 +1,38 @@
1
+ """
2
+ 执行器模块
3
+
4
+ 提供任务执行的核心功能,支持单进程和多进程两种模式。
5
+
6
+ 主要组件:
7
+ - TaskExecutor: 任务执行器(新)
8
+ - ProcessOrchestrator: 多进程编排器
9
+ - ExecutorCore: 核心执行逻辑
10
+ """
11
+
12
+ from .core import ExecutorCore
13
+ from .orchestrator import ProcessConfig, ProcessOrchestrator
14
+ from .task_executor import TaskExecutor
15
+
16
+ # 保留 UnifiedExecutor 作为兼容(已废弃)
17
+ try:
18
+ from .executor import UnifiedExecutor
19
+ except ImportError:
20
+ # 如果导入失败,提供一个废弃提示
21
+ class UnifiedExecutor:
22
+ def __init__(self, *args, **kwargs):
23
+ raise DeprecationWarning(
24
+ "UnifiedExecutor is deprecated. "
25
+ "Use TaskExecutor for single task execution, "
26
+ "or ProcessOrchestrator for multi-process management."
27
+ )
28
+
29
+ __all__ = [
30
+ # 新的推荐类
31
+ 'TaskExecutor',
32
+ 'ProcessOrchestrator',
33
+ 'ProcessConfig',
34
+ 'ExecutorCore',
35
+
36
+ # 废弃但保留兼容性
37
+ 'UnifiedExecutor',
38
+ ]