hello-datap-component-base 0.2.0__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.
- hello_datap_component_base/__init__.py +31 -0
- hello_datap_component_base/base.py +211 -0
- hello_datap_component_base/cli.py +276 -0
- hello_datap_component_base/config.py +169 -0
- hello_datap_component_base/discover.py +187 -0
- hello_datap_component_base/logger.py +290 -0
- hello_datap_component_base/mns_client.py +286 -0
- hello_datap_component_base/runner.py +247 -0
- hello_datap_component_base-0.2.0.dist-info/METADATA +596 -0
- hello_datap_component_base-0.2.0.dist-info/RECORD +13 -0
- hello_datap_component_base-0.2.0.dist-info/WHEEL +5 -0
- hello_datap_component_base-0.2.0.dist-info/entry_points.txt +2 -0
- hello_datap_component_base-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""
|
|
2
|
+
阿里云 MNS 消息队列客户端
|
|
3
|
+
"""
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import time
|
|
7
|
+
from typing import Dict, Any, Optional
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MNSClient:
|
|
14
|
+
"""阿里云 MNS 消息队列客户端"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
queue_name: str = "aiinfra-data-process-component-result-queue",
|
|
19
|
+
max_retries: int = 3,
|
|
20
|
+
retry_delay: float = 1.0,
|
|
21
|
+
retry_backoff: float = 2.0
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
初始化 MNS 客户端
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
queue_name: 队列名称,默认为 aiinfra-data-process-component-result-queue
|
|
28
|
+
max_retries: 最大重试次数,默认 3 次(可通过环境变量 MNS_MAX_RETRIES 配置)
|
|
29
|
+
retry_delay: 初始重试延迟(秒),默认 1.0 秒(可通过环境变量 MNS_RETRY_DELAY 配置)
|
|
30
|
+
retry_backoff: 重试延迟的指数退避倍数,默认 2.0(可通过环境变量 MNS_RETRY_BACKOFF 配置)
|
|
31
|
+
"""
|
|
32
|
+
self.queue_name = queue_name
|
|
33
|
+
|
|
34
|
+
# 从环境变量读取重试配置,如果没有则使用默认值
|
|
35
|
+
self.max_retries = int(os.environ.get('MNS_MAX_RETRIES', max_retries))
|
|
36
|
+
self.retry_delay = float(os.environ.get('MNS_RETRY_DELAY', retry_delay))
|
|
37
|
+
self.retry_backoff = float(os.environ.get('MNS_RETRY_BACKOFF', retry_backoff))
|
|
38
|
+
|
|
39
|
+
self._client = None
|
|
40
|
+
self._queue = None
|
|
41
|
+
self._initialized = False
|
|
42
|
+
self._Message = None # Message 类,延迟加载
|
|
43
|
+
|
|
44
|
+
def _init_client(self):
|
|
45
|
+
"""初始化 MNS 客户端(延迟加载)"""
|
|
46
|
+
if self._initialized:
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# 尝试导入阿里云 MNS SDK
|
|
51
|
+
from mns.account import Account
|
|
52
|
+
from mns.queue import Queue, Message
|
|
53
|
+
|
|
54
|
+
# 保存 Message 类供后续使用
|
|
55
|
+
self._Message = Message
|
|
56
|
+
|
|
57
|
+
# 从环境变量获取 MNS 配置
|
|
58
|
+
endpoint = os.environ.get('MNS_ENDPOINT')
|
|
59
|
+
access_key_id = os.environ.get('MNS_ACCESS_KEY_ID')
|
|
60
|
+
access_key_secret = os.environ.get('MNS_ACCESS_KEY_SECRET')
|
|
61
|
+
|
|
62
|
+
if not all([endpoint, access_key_id, access_key_secret]):
|
|
63
|
+
# 如果 MNS_ENDPOINT 不存在,静默跳过(已在 get_mns_client 中检查)
|
|
64
|
+
# 如果 MNS_ENDPOINT 存在但其他配置不完整,记录警告
|
|
65
|
+
if endpoint:
|
|
66
|
+
logger.warning(
|
|
67
|
+
"MNS 配置不完整,无法发送消息到队列。"
|
|
68
|
+
"需要设置环境变量: MNS_ACCESS_KEY_ID, MNS_ACCESS_KEY_SECRET"
|
|
69
|
+
)
|
|
70
|
+
self._initialized = True # 标记为已初始化,避免重复警告
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
# 创建 MNS 账户
|
|
74
|
+
account = Account(endpoint, access_key_id, access_key_secret)
|
|
75
|
+
# 获取队列(使用 get_queue 方法)
|
|
76
|
+
self._queue = account.get_queue(self.queue_name)
|
|
77
|
+
self._client = account
|
|
78
|
+
self._initialized = True
|
|
79
|
+
logger.info(f"MNS 客户端初始化成功,队列: {self.queue_name}")
|
|
80
|
+
|
|
81
|
+
except ImportError:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"未安装阿里云 MNS SDK,无法发送消息到队列。"
|
|
84
|
+
"请安装: pip install aliyun-mns"
|
|
85
|
+
)
|
|
86
|
+
self._initialized = True # 标记为已初始化,避免重复警告
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"初始化 MNS 客户端失败: {e}")
|
|
89
|
+
self._initialized = True # 标记为已初始化,避免重复警告
|
|
90
|
+
|
|
91
|
+
def _is_retryable_exception(self, exception: Exception) -> bool:
|
|
92
|
+
"""
|
|
93
|
+
判断异常是否可重试
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
exception: 异常对象
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
是否可重试
|
|
100
|
+
"""
|
|
101
|
+
exception_str = str(exception)
|
|
102
|
+
exception_type = type(exception).__name__
|
|
103
|
+
|
|
104
|
+
# 网络相关异常,可重试
|
|
105
|
+
retryable_keywords = [
|
|
106
|
+
'NetworkException',
|
|
107
|
+
'NetWorkException',
|
|
108
|
+
'Connection reset',
|
|
109
|
+
'Connection refused',
|
|
110
|
+
'Connection timeout',
|
|
111
|
+
'timeout',
|
|
112
|
+
'ConnectionError',
|
|
113
|
+
'ConnectTimeout',
|
|
114
|
+
'ReadTimeout',
|
|
115
|
+
'Errno 104', # Connection reset by peer
|
|
116
|
+
'Errno 111', # Connection refused
|
|
117
|
+
'Errno 110', # Connection timed out
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
# 检查异常类型和消息中是否包含可重试的关键词
|
|
121
|
+
for keyword in retryable_keywords:
|
|
122
|
+
if keyword in exception_type or keyword in exception_str:
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def _send_message_once(self, message_body: str) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
发送消息的单次尝试
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
message_body: JSON 格式的消息字符串
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
是否发送成功
|
|
136
|
+
"""
|
|
137
|
+
# 创建 Message 对象(MNS SDK 要求使用 Message 对象)
|
|
138
|
+
if self._Message is None:
|
|
139
|
+
from mns.queue import Message
|
|
140
|
+
self._Message = Message
|
|
141
|
+
|
|
142
|
+
msg = self._Message(message_body)
|
|
143
|
+
|
|
144
|
+
# 发送消息
|
|
145
|
+
self._queue.send_message(msg)
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
def send_message(self, message: Dict[str, Any]) -> bool:
|
|
149
|
+
"""
|
|
150
|
+
发送消息到队列(带重试逻辑)
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
message: 要发送的消息字典
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
是否发送成功
|
|
157
|
+
"""
|
|
158
|
+
self._init_client()
|
|
159
|
+
|
|
160
|
+
if not self._queue:
|
|
161
|
+
logger.warning("MNS 队列未初始化,跳过消息发送")
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
# 将消息转换为 JSON 字符串
|
|
165
|
+
message_body = json.dumps(message, ensure_ascii=False)
|
|
166
|
+
|
|
167
|
+
# 重试逻辑
|
|
168
|
+
last_exception = None
|
|
169
|
+
delay = self.retry_delay
|
|
170
|
+
|
|
171
|
+
for attempt in range(self.max_retries + 1): # 总共尝试 max_retries + 1 次
|
|
172
|
+
try:
|
|
173
|
+
success = self._send_message_once(message_body)
|
|
174
|
+
if success:
|
|
175
|
+
if attempt > 0:
|
|
176
|
+
logger.info(
|
|
177
|
+
f"消息已成功发送到队列 {self.queue_name} "
|
|
178
|
+
f"(第 {attempt + 1} 次尝试)"
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
logger.info(f"消息已发送到队列 {self.queue_name}")
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
except Exception as e:
|
|
185
|
+
last_exception = e
|
|
186
|
+
|
|
187
|
+
# 判断是否可重试
|
|
188
|
+
if not self._is_retryable_exception(e):
|
|
189
|
+
# 不可重试的异常,直接返回失败
|
|
190
|
+
logger.error(
|
|
191
|
+
f"发送消息到队列失败(不可重试的异常): {e}",
|
|
192
|
+
exc_info=True
|
|
193
|
+
)
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
# 可重试的异常
|
|
197
|
+
if attempt < self.max_retries:
|
|
198
|
+
# 还有重试机会
|
|
199
|
+
logger.warning(
|
|
200
|
+
f"发送消息到队列失败(第 {attempt + 1} 次尝试): {e},"
|
|
201
|
+
f"{delay:.2f} 秒后重试..."
|
|
202
|
+
)
|
|
203
|
+
time.sleep(delay)
|
|
204
|
+
delay *= self.retry_backoff # 指数退避
|
|
205
|
+
else:
|
|
206
|
+
# 已用完所有重试机会
|
|
207
|
+
logger.error(
|
|
208
|
+
f"发送消息到队列失败(已重试 {self.max_retries} 次): {e}",
|
|
209
|
+
exc_info=True
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# 所有重试都失败了
|
|
213
|
+
if last_exception:
|
|
214
|
+
logger.error(
|
|
215
|
+
f"发送消息到队列最终失败(共尝试 {self.max_retries + 1} 次): {last_exception}",
|
|
216
|
+
exc_info=True
|
|
217
|
+
)
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
def send_result(
|
|
221
|
+
self,
|
|
222
|
+
code: int,
|
|
223
|
+
message: str,
|
|
224
|
+
work_flow_id: Optional[int],
|
|
225
|
+
work_flow_instance_id: Optional[int],
|
|
226
|
+
task_id: Optional[str],
|
|
227
|
+
output: Optional[Dict[str, Any]] = None,
|
|
228
|
+
processing_time: Optional[float] = None
|
|
229
|
+
) -> bool:
|
|
230
|
+
"""
|
|
231
|
+
发送处理结果到队列
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
code: 返回码,0 表示成功,非 0 表示失败
|
|
235
|
+
message: 返回消息
|
|
236
|
+
work_flow_id: 工作流ID
|
|
237
|
+
work_flow_instance_id: 工作流实例ID
|
|
238
|
+
task_id: 任务ID
|
|
239
|
+
output: 用户程序的输出结果(正常时为结果,异常时为 None)
|
|
240
|
+
processing_time: 处理时间(秒)
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
是否发送成功
|
|
244
|
+
"""
|
|
245
|
+
result_message = {
|
|
246
|
+
"code": code,
|
|
247
|
+
"message": message,
|
|
248
|
+
"data": {
|
|
249
|
+
"work_flow_id": work_flow_id,
|
|
250
|
+
"work_flow_instance_id": work_flow_instance_id,
|
|
251
|
+
"task_id": task_id,
|
|
252
|
+
"out_put": output
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if processing_time is not None:
|
|
256
|
+
result_message["processing_time"] = processing_time
|
|
257
|
+
|
|
258
|
+
return self.send_message(result_message)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def get_mns_client(queue_name: Optional[str] = None) -> Optional[MNSClient]:
|
|
262
|
+
"""
|
|
263
|
+
获取 MNS 客户端实例
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
queue_name: 队列名称,如果为 None 则使用默认队列名
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
MNSClient 实例,如果配置不完整则返回 None
|
|
270
|
+
"""
|
|
271
|
+
# 如果环境变量中没有 MNS_ENDPOINT 配置,则不发送 MNS,直接返回 None
|
|
272
|
+
if not os.environ.get('MNS_ENDPOINT'):
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
if queue_name is None:
|
|
276
|
+
queue_name = os.environ.get('MNS_QUEUE_NAME', 'aiinfra-data-process-component-result-queue')
|
|
277
|
+
|
|
278
|
+
client = MNSClient(queue_name)
|
|
279
|
+
client._init_client()
|
|
280
|
+
|
|
281
|
+
# 如果客户端未正确初始化,返回 None
|
|
282
|
+
if not client._queue:
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
return client
|
|
286
|
+
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import signal
|
|
3
|
+
import sys
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import Optional, Dict, Any, Type
|
|
6
|
+
from .base import BaseService, ServiceConfig
|
|
7
|
+
from .config import ServerConfig
|
|
8
|
+
from .discover import get_single_service_class
|
|
9
|
+
from .logger import setup_logging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ServiceRunner:
|
|
13
|
+
"""服务运行器"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, config_path: str, class_name: Optional[str] = None):
|
|
16
|
+
"""
|
|
17
|
+
初始化运行器
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
config_path: 配置文件路径
|
|
21
|
+
class_name: 指定的服务类名
|
|
22
|
+
"""
|
|
23
|
+
self.config_path = config_path
|
|
24
|
+
self.class_name = class_name
|
|
25
|
+
self.config: Optional[ServerConfig] = None
|
|
26
|
+
self.service_class: Optional[Type[BaseService]] = None
|
|
27
|
+
self.service_instance: Optional[BaseService] = None
|
|
28
|
+
|
|
29
|
+
def load_config(self):
|
|
30
|
+
"""加载配置"""
|
|
31
|
+
self.config = ServerConfig.from_file(self.config_path)
|
|
32
|
+
|
|
33
|
+
def discover_service(self):
|
|
34
|
+
"""发现服务类"""
|
|
35
|
+
import os
|
|
36
|
+
# 使用当前工作目录作为搜索路径
|
|
37
|
+
search_path = os.getcwd()
|
|
38
|
+
self.service_class = get_single_service_class(
|
|
39
|
+
search_path=search_path,
|
|
40
|
+
class_name=self.class_name
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def setup_environment(self):
|
|
44
|
+
"""设置运行环境"""
|
|
45
|
+
# 设置日志
|
|
46
|
+
setup_logging(
|
|
47
|
+
level=self.config.runtime_env.env_vars.get("LOG_LEVEL", "INFO")
|
|
48
|
+
if self.config.runtime_env and self.config.runtime_env.env_vars
|
|
49
|
+
else "INFO",
|
|
50
|
+
json_format=False,
|
|
51
|
+
service_name=self.config.name,
|
|
52
|
+
version=self.config.version
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# 安装pip包(如果配置了)
|
|
56
|
+
self._install_pip_packages()
|
|
57
|
+
|
|
58
|
+
def _install_pip_packages(self):
|
|
59
|
+
"""安装runtime_env中指定的pip包"""
|
|
60
|
+
if not self.config.runtime_env or not self.config.runtime_env.pip:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
pip_packages = self.config.runtime_env.pip
|
|
64
|
+
if not isinstance(pip_packages, list) or len(pip_packages) == 0:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
print(f"Installing pip packages: {pip_packages}")
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
# 使用 subprocess 调用 pip install
|
|
71
|
+
# 使用 -q 参数减少输出,--disable-pip-version-check 禁用版本检查警告
|
|
72
|
+
cmd = [
|
|
73
|
+
sys.executable, "-m", "pip", "install",
|
|
74
|
+
"-q", "--disable-pip-version-check"
|
|
75
|
+
] + pip_packages
|
|
76
|
+
|
|
77
|
+
result = subprocess.run(
|
|
78
|
+
cmd,
|
|
79
|
+
check=True,
|
|
80
|
+
capture_output=True,
|
|
81
|
+
text=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
print(f"✅ Successfully installed packages: {', '.join(pip_packages)}")
|
|
85
|
+
|
|
86
|
+
except subprocess.CalledProcessError as e:
|
|
87
|
+
error_msg = f"Failed to install pip packages: {pip_packages}\n"
|
|
88
|
+
if e.stdout:
|
|
89
|
+
error_msg += f"stdout: {e.stdout}\n"
|
|
90
|
+
if e.stderr:
|
|
91
|
+
error_msg += f"stderr: {e.stderr}\n"
|
|
92
|
+
print(f"❌ {error_msg}", file=sys.stderr)
|
|
93
|
+
raise RuntimeError(f"Failed to install required pip packages: {error_msg}")
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print(f"❌ Error installing pip packages: {e}", file=sys.stderr)
|
|
96
|
+
raise RuntimeError(f"Failed to install required pip packages: {e}")
|
|
97
|
+
|
|
98
|
+
def create_service_instance(self):
|
|
99
|
+
"""创建服务实例"""
|
|
100
|
+
# 创建 ServiceConfig
|
|
101
|
+
runtime_env_dict = None
|
|
102
|
+
if self.config.runtime_env:
|
|
103
|
+
runtime_env_dict = self.config.runtime_env.model_dump(exclude_none=True)
|
|
104
|
+
# 如果转换后的字典为空,设置为None
|
|
105
|
+
if not runtime_env_dict:
|
|
106
|
+
runtime_env_dict = None
|
|
107
|
+
|
|
108
|
+
service_config = ServiceConfig(
|
|
109
|
+
name=self.config.name,
|
|
110
|
+
version=self.config.version,
|
|
111
|
+
params=self.config.params,
|
|
112
|
+
runtime_env=runtime_env_dict,
|
|
113
|
+
work_flow_id=self.config.work_flow_id,
|
|
114
|
+
work_flow_instance_id=self.config.work_flow_instance_id,
|
|
115
|
+
task_id=self.config.task_id
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# 创建服务实例
|
|
119
|
+
self.service_instance = self.service_class(service_config)
|
|
120
|
+
|
|
121
|
+
async def run_async(self):
|
|
122
|
+
"""异步运行服务并执行一次处理"""
|
|
123
|
+
print(f"Starting service: {self.config.name}")
|
|
124
|
+
if self.config.version:
|
|
125
|
+
print(f"Version: {self.config.version}")
|
|
126
|
+
print(f"Config: {self.config.model_dump_json(indent=2)}")
|
|
127
|
+
print("\n" + "=" * 60)
|
|
128
|
+
print("Processing request...")
|
|
129
|
+
print("=" * 60 + "\n")
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
# 准备输入数据
|
|
133
|
+
# 从配置文件的 params 中获取,如果不存在则使用默认值
|
|
134
|
+
params = self.config.params
|
|
135
|
+
if params is None:
|
|
136
|
+
params = {}
|
|
137
|
+
|
|
138
|
+
# 执行一次处理(handle_request 会封装结果,包括异常情况)
|
|
139
|
+
result = await self.service_instance.handle_request(params)
|
|
140
|
+
|
|
141
|
+
# 输出结果
|
|
142
|
+
import json
|
|
143
|
+
print("=" * 60)
|
|
144
|
+
print("Processing Result:")
|
|
145
|
+
print("=" * 60)
|
|
146
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
147
|
+
print("=" * 60)
|
|
148
|
+
|
|
149
|
+
# 发送结果到 MNS 队列
|
|
150
|
+
self._send_result_to_mns(result)
|
|
151
|
+
|
|
152
|
+
print("Service completed successfully.")
|
|
153
|
+
print("=" * 60)
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
print(f"\nError processing request: {e}", file=sys.stderr)
|
|
157
|
+
import traceback
|
|
158
|
+
traceback.print_exc()
|
|
159
|
+
|
|
160
|
+
# 即使发生异常,也要尝试发送错误结果到队列
|
|
161
|
+
try:
|
|
162
|
+
error_result = {
|
|
163
|
+
"code": -1,
|
|
164
|
+
"message": str(e),
|
|
165
|
+
"data": {
|
|
166
|
+
"work_flow_id": self.config.work_flow_id,
|
|
167
|
+
"work_flow_instance_id": self.config.work_flow_instance_id,
|
|
168
|
+
"task_id": self.config.task_id,
|
|
169
|
+
"out_put": None
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
self._send_result_to_mns(error_result)
|
|
173
|
+
except Exception as mns_error:
|
|
174
|
+
print(f"Failed to send error result to MNS: {mns_error}", file=sys.stderr)
|
|
175
|
+
|
|
176
|
+
raise
|
|
177
|
+
|
|
178
|
+
def run(self):
|
|
179
|
+
"""运行服务(主入口)"""
|
|
180
|
+
try:
|
|
181
|
+
# 加载配置
|
|
182
|
+
self.load_config()
|
|
183
|
+
|
|
184
|
+
# 发现服务类
|
|
185
|
+
self.discover_service()
|
|
186
|
+
|
|
187
|
+
# 设置环境
|
|
188
|
+
self.setup_environment()
|
|
189
|
+
|
|
190
|
+
# 创建服务实例
|
|
191
|
+
self.create_service_instance()
|
|
192
|
+
|
|
193
|
+
# 运行服务
|
|
194
|
+
asyncio.run(self.run_async())
|
|
195
|
+
|
|
196
|
+
except KeyboardInterrupt:
|
|
197
|
+
print("\nService stopped by user")
|
|
198
|
+
except Exception as e:
|
|
199
|
+
print(f"Error running service: {e}", file=sys.stderr)
|
|
200
|
+
sys.exit(1)
|
|
201
|
+
|
|
202
|
+
def _send_result_to_mns(self, result: Dict[str, Any]):
|
|
203
|
+
"""
|
|
204
|
+
发送结果到 MNS 队列
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
result: 封装后的结果字典
|
|
208
|
+
"""
|
|
209
|
+
try:
|
|
210
|
+
from .mns_client import get_mns_client
|
|
211
|
+
import os
|
|
212
|
+
|
|
213
|
+
# 如果环境变量中没有 MNS_ENDPOINT 配置,则不发送 MNS,静默跳过
|
|
214
|
+
if not os.environ.get('MNS_ENDPOINT'):
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
client = get_mns_client()
|
|
218
|
+
if client:
|
|
219
|
+
# result 已经是封装好的格式,直接发送
|
|
220
|
+
success = client.send_message(result)
|
|
221
|
+
if success:
|
|
222
|
+
print("✅ Result sent to MNS queue successfully")
|
|
223
|
+
else:
|
|
224
|
+
print("⚠️ Failed to send result to MNS queue")
|
|
225
|
+
# 如果 client 为 None(配置不完整),静默跳过,不打印警告
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"⚠️ Error sending result to MNS queue: {e}", file=sys.stderr)
|
|
228
|
+
|
|
229
|
+
async def process_request(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
230
|
+
"""
|
|
231
|
+
处理请求(供外部调用)
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
data: 输入数据
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
处理结果
|
|
238
|
+
"""
|
|
239
|
+
if not self.service_instance:
|
|
240
|
+
raise RuntimeError("Service not started")
|
|
241
|
+
|
|
242
|
+
result = await self.service_instance.handle_request(data)
|
|
243
|
+
|
|
244
|
+
# 发送结果到 MNS 队列
|
|
245
|
+
self._send_result_to_mns(result)
|
|
246
|
+
|
|
247
|
+
return result
|