polaris-python 0.0.1.dev3__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.
- polaris/__init__.py +74 -0
- polaris/_version.py +16 -0
- polaris/api/__init__.py +55 -0
- polaris/api/_log_file.py +232 -0
- polaris/api/config.py +145 -0
- polaris/api/consumer.py +240 -0
- polaris/api/log.py +414 -0
- polaris/api/lossless.py +73 -0
- polaris/api/provider.py +152 -0
- polaris/api/skill.py +162 -0
- polaris/pkg/__init__.py +15 -0
- polaris/pkg/common/__init__.py +15 -0
- polaris/pkg/common/plugin.py +33 -0
- polaris/pkg/config/__init__.py +15 -0
- polaris/pkg/config/api.py +1205 -0
- polaris/pkg/config/default.py +92 -0
- polaris/pkg/config/event.py +76 -0
- polaris/pkg/config/loader.py +392 -0
- polaris/pkg/config/stat_reporter.py +100 -0
- polaris/pkg/flow/__init__.py +15 -0
- polaris/pkg/flow/admin_server.py +104 -0
- polaris/pkg/flow/async_flow.py +55 -0
- polaris/pkg/flow/base_flow.py +111 -0
- polaris/pkg/flow/impl.py +1277 -0
- polaris/pkg/flow/lossless.py +605 -0
- polaris/pkg/flow/notify.py +46 -0
- polaris/pkg/flow/register.py +210 -0
- polaris/pkg/flow/skill.py +110 -0
- polaris/pkg/flow/sync_flow.py +20 -0
- polaris/pkg/model/__init__.py +15 -0
- polaris/pkg/model/context.py +53 -0
- polaris/pkg/model/contract.py +89 -0
- polaris/pkg/model/core.py +51 -0
- polaris/pkg/model/engine.py +83 -0
- polaris/pkg/model/error.py +94 -0
- polaris/pkg/model/event.py +159 -0
- polaris/pkg/model/lossless.py +102 -0
- polaris/pkg/model/rule.py +152 -0
- polaris/pkg/model/server.py +288 -0
- polaris/pkg/model/service.py +820 -0
- polaris/pkg/model/skill.py +179 -0
- polaris/pkg/model/stat.py +168 -0
- polaris/pkg/model/util.py +58 -0
- polaris/pkg/version/__init__.py +18 -0
- polaris/plugin/__init__.py +32 -0
- polaris/plugin/circuit_breaker/__init__.py +19 -0
- polaris/plugin/circuit_breaker/base.py +54 -0
- polaris/plugin/circuit_breaker/composite.py +265 -0
- polaris/plugin/circuit_breaker/config.py +36 -0
- polaris/plugin/event/__init__.py +21 -0
- polaris/plugin/event/pushgateway.py +202 -0
- polaris/plugin/load_balancer/__init__.py +24 -0
- polaris/plugin/load_balancer/base.py +30 -0
- polaris/plugin/load_balancer/random.py +39 -0
- polaris/plugin/load_balancer/weighted_random.py +50 -0
- polaris/plugin/local_registry/__init__.py +18 -0
- polaris/plugin/local_registry/base.py +47 -0
- polaris/plugin/local_registry/file_cache.py +335 -0
- polaris/plugin/local_registry/local_cache.py +433 -0
- polaris/plugin/server_connector/__init__.py +20 -0
- polaris/plugin/server_connector/base.py +104 -0
- polaris/plugin/server_connector/convert.py +149 -0
- polaris/plugin/server_connector/grpc_connector/__init__.py +21 -0
- polaris/plugin/server_connector/grpc_connector/connection.py +401 -0
- polaris/plugin/server_connector/grpc_connector/grpc_client.py +384 -0
- polaris/plugin/server_connector/grpc_connector/tls.py +45 -0
- polaris/plugin/service_router/__init__.py +28 -0
- polaris/plugin/service_router/base.py +105 -0
- polaris/plugin/service_router/isolated.py +35 -0
- polaris/plugin/service_router/metadata.py +84 -0
- polaris/plugin/service_router/recover.py +62 -0
- polaris/plugin/service_router/util.py +54 -0
- polaris/plugin/skill/__init__.py +16 -0
- polaris/plugin/skill/connector.py +127 -0
- polaris/plugin/skill/persistence.py +94 -0
- polaris/plugin/stat_reporter/__init__.py +16 -0
- polaris/plugin/stat_reporter/base.py +34 -0
- polaris/plugin/stat_reporter/prometheus.py +375 -0
- polaris/plugin_manager.py +146 -0
- polaris_python-0.0.1.dev3.dist-info/METADATA +213 -0
- polaris_python-0.0.1.dev3.dist-info/RECORD +84 -0
- polaris_python-0.0.1.dev3.dist-info/WHEEL +5 -0
- polaris_python-0.0.1.dev3.dist-info/licenses/LICENSE +18 -0
- polaris_python-0.0.1.dev3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1205 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
12
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
13
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
14
|
+
# specific language governing permissions and limitations under the License.
|
|
15
|
+
|
|
16
|
+
import ipaddress
|
|
17
|
+
from typing import Dict
|
|
18
|
+
from typing import List
|
|
19
|
+
|
|
20
|
+
from polaris.pkg.config.default import DefaultConf
|
|
21
|
+
from polaris.pkg.model import util
|
|
22
|
+
from polaris.pkg.model.core import ServiceKey
|
|
23
|
+
from polaris.pkg.model.error import ErrorCode
|
|
24
|
+
from polaris.pkg.model.error import SDKError
|
|
25
|
+
from polaris.pkg.model.service import RunMode
|
|
26
|
+
from polaris.pkg.config.stat_reporter import AdminConfig, prometheus_config
|
|
27
|
+
from polaris.pkg.config.event import pushgateway_event_config
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# 默认集群类型
|
|
31
|
+
class ClusterType:
|
|
32
|
+
BuiltinCluster = "builtin"
|
|
33
|
+
DiscoverCluster = "discover"
|
|
34
|
+
HealthCheckCluster = "healthCheck"
|
|
35
|
+
MonitorCluster = "monitor"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SelfVerifier:
|
|
39
|
+
def verify(self):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def set_default(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ClusterService:
|
|
47
|
+
def __init__(self, service_key: ServiceKey, cluster_type: str):
|
|
48
|
+
self.service_key = service_key
|
|
49
|
+
self.cluster_type = cluster_type
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# 单个服务集群配置
|
|
53
|
+
class ServerClusterConfig:
|
|
54
|
+
def __init__(self, namespace: str | None = None, service: str | None = None,
|
|
55
|
+
refresh_interval: int | None = None):
|
|
56
|
+
# Namespace string `yaml:"namespace"`
|
|
57
|
+
# Service string `yaml:"service"`
|
|
58
|
+
# RefreshInterval *time.Duration `yaml:"refreshInterval"`
|
|
59
|
+
self.namespace = namespace
|
|
60
|
+
self.service = service
|
|
61
|
+
self.refresh_interval = refresh_interval
|
|
62
|
+
|
|
63
|
+
def get_namespace(self):
|
|
64
|
+
return self.namespace
|
|
65
|
+
|
|
66
|
+
def get_service(self):
|
|
67
|
+
return self.service
|
|
68
|
+
|
|
69
|
+
def get_refresh_interval(self):
|
|
70
|
+
return self.refresh_interval
|
|
71
|
+
|
|
72
|
+
def verify(self):
|
|
73
|
+
if self.refresh_interval is None or self.refresh_interval < DefaultConf.DefaultMinTimingInterval:
|
|
74
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
75
|
+
"ServerClusterConfig: refreshInterval can not be empty and must greater than %d" %
|
|
76
|
+
DefaultConf.DefaultMinTimingInterval)
|
|
77
|
+
|
|
78
|
+
def set_default(self):
|
|
79
|
+
if self.namespace is None:
|
|
80
|
+
self.namespace = ""
|
|
81
|
+
if self.service is None:
|
|
82
|
+
self.service = ""
|
|
83
|
+
if self.refresh_interval is None:
|
|
84
|
+
self.refresh_interval = DefaultConf.DefaultServerServiceRefreshInterval
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_server_cluster_conf(service_key: ServiceKey):
|
|
88
|
+
return ServerClusterConfig(service_key.get_namespace(), service_key.get_service())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# 系统配置
|
|
92
|
+
class SystemConfig(SelfVerifier):
|
|
93
|
+
"""System options with cluster fields matching the Java and Go SDK configuration."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, mode: int = 0, discover_cluster: ServerClusterConfig = None,
|
|
96
|
+
health_check_cluster: ServerClusterConfig = None, monitor_cluster: ServerClusterConfig = None):
|
|
97
|
+
self.mode = mode
|
|
98
|
+
self.discover_cluster = discover_cluster
|
|
99
|
+
self.health_check_cluster = health_check_cluster
|
|
100
|
+
self.monitor_cluster = monitor_cluster
|
|
101
|
+
|
|
102
|
+
def get_mode(self) -> int:
|
|
103
|
+
return self.mode
|
|
104
|
+
|
|
105
|
+
def get_discover_cluster(self) -> ServerClusterConfig:
|
|
106
|
+
"""Return the discovery server cluster configuration."""
|
|
107
|
+
return self.discover_cluster
|
|
108
|
+
|
|
109
|
+
def get_health_check_cluster(self) -> ServerClusterConfig:
|
|
110
|
+
"""Return the health-check server cluster configuration."""
|
|
111
|
+
return self.health_check_cluster
|
|
112
|
+
|
|
113
|
+
def get_monitor_cluster(self) -> ServerClusterConfig:
|
|
114
|
+
"""Return the monitoring server cluster configuration."""
|
|
115
|
+
return self.monitor_cluster
|
|
116
|
+
|
|
117
|
+
def verify(self):
|
|
118
|
+
if self.mode != RunMode.ModeNoAgent and self.mode != RunMode.ModeWithAgent:
|
|
119
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
120
|
+
"SystemConfig: global.system.mode=%d is invalid, you can use no-agent(%d) or with-agent(%d)" % (
|
|
121
|
+
self.mode, RunMode.ModeNoAgent, RunMode.ModeWithAgent))
|
|
122
|
+
self.discover_cluster.verify()
|
|
123
|
+
self.health_check_cluster.verify()
|
|
124
|
+
self.monitor_cluster.verify()
|
|
125
|
+
|
|
126
|
+
def set_default(self):
|
|
127
|
+
if self.discover_cluster is None:
|
|
128
|
+
self.discover_cluster = ServerClusterConfig()
|
|
129
|
+
if self.health_check_cluster is None:
|
|
130
|
+
self.health_check_cluster = ServerClusterConfig()
|
|
131
|
+
if self.monitor_cluster is None:
|
|
132
|
+
self.monitor_cluster = ServerClusterConfig()
|
|
133
|
+
self.discover_cluster.set_default()
|
|
134
|
+
self.health_check_cluster.set_default()
|
|
135
|
+
self.monitor_cluster.set_default()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# API访问相关的配置
|
|
139
|
+
class APIConfig(SelfVerifier):
|
|
140
|
+
"""Configure API access and the SDK's own address, independently of instance hosts."""
|
|
141
|
+
|
|
142
|
+
def __init__(self, timeout: int | None = None, bind_intf: str = "", bind_ip: str = "",
|
|
143
|
+
report_interval: int | None = None,
|
|
144
|
+
max_retry_times: int = 0,
|
|
145
|
+
retry_interval: int | None = None):
|
|
146
|
+
# Timeout *time.Duration `yaml:"timeout"`
|
|
147
|
+
# BindIntf string `yaml:"bindIf"`
|
|
148
|
+
# BindIP string `yaml:"bindIP"`
|
|
149
|
+
# BindIPValue string `yaml:"_"`
|
|
150
|
+
# ReportInterval *time.Duration `yaml:"reportInterval"`
|
|
151
|
+
# MaxRetryTimes int `yaml:"maxRetryTimes"`
|
|
152
|
+
# RetryInterval *time.Duration `yaml:"retryInterval"`
|
|
153
|
+
self.timeout = timeout
|
|
154
|
+
self.bind_intf = bind_intf
|
|
155
|
+
self.bind_ip = bind_ip
|
|
156
|
+
self.bind_ip_value = ""
|
|
157
|
+
self.report_interval = report_interval
|
|
158
|
+
self.max_retry_times = max_retry_times
|
|
159
|
+
self.retry_interval = retry_interval
|
|
160
|
+
|
|
161
|
+
def get_timeout(self):
|
|
162
|
+
return self.timeout
|
|
163
|
+
|
|
164
|
+
def get_bind_intf(self):
|
|
165
|
+
return self.bind_intf
|
|
166
|
+
|
|
167
|
+
def get_bind_ip(self):
|
|
168
|
+
"""Return the explicit or startup-resolved SDK address, not an instance host."""
|
|
169
|
+
return self.bind_ip_value
|
|
170
|
+
|
|
171
|
+
def get_report_interval(self):
|
|
172
|
+
return self.report_interval
|
|
173
|
+
|
|
174
|
+
def get_max_retry_times(self):
|
|
175
|
+
return self.max_retry_times
|
|
176
|
+
|
|
177
|
+
def get_retry_interval(self):
|
|
178
|
+
return self.retry_interval
|
|
179
|
+
|
|
180
|
+
def verify(self):
|
|
181
|
+
if self.max_retry_times < 0:
|
|
182
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
183
|
+
"APIConfig: global.api.maxRetryTimes must be greater than 0")
|
|
184
|
+
if not isinstance(self.bind_ip, str) or not isinstance(self.bind_intf, str):
|
|
185
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
186
|
+
"APIConfig: global.api.bindIP and bindIf must be strings")
|
|
187
|
+
self.bind_ip_value = self.bind_ip
|
|
188
|
+
if not self.bind_ip and self.bind_intf:
|
|
189
|
+
try:
|
|
190
|
+
self.bind_ip_value = util.get_ip(self.bind_intf)
|
|
191
|
+
except (OSError, ValueError) as error:
|
|
192
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
193
|
+
"APIConfig: can not get ip from provided bind interface %s" % self.bind_intf) from error
|
|
194
|
+
if self.retry_interval < DefaultConf.DefaultAPIRetryInterval:
|
|
195
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
196
|
+
"APIConfig: global.api.retryInterval must be greater than %d" %
|
|
197
|
+
DefaultConf.DefaultAPIRetryInterval)
|
|
198
|
+
|
|
199
|
+
def set_default(self):
|
|
200
|
+
if self.timeout is None:
|
|
201
|
+
self.timeout = DefaultConf.DefaultAPIInvokeTimeout
|
|
202
|
+
if self.report_interval is None:
|
|
203
|
+
self.report_interval = DefaultConf.DefaultReportClientIntervalDuration
|
|
204
|
+
if self.retry_interval is None:
|
|
205
|
+
self.retry_interval = DefaultConf.DefaultAPIRetryInterval
|
|
206
|
+
if not self.max_retry_times:
|
|
207
|
+
self.max_retry_times = DefaultConf.DefaultAPIMaxRetryTimes
|
|
208
|
+
self.bind_ip_value = self.bind_ip
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# global.statReporter
|
|
212
|
+
class StatReporterConfig:
|
|
213
|
+
def __init__(self, enable: bool | None = None, chain: List[str] = None, plugin: Dict = None):
|
|
214
|
+
# //是否启动上报
|
|
215
|
+
# Enable *bool `yaml:"enable"`
|
|
216
|
+
# //上报插件链
|
|
217
|
+
# Chain []string `yaml:"chain"`
|
|
218
|
+
# // 插件相关配置
|
|
219
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
220
|
+
self.enable = enable
|
|
221
|
+
if chain is None:
|
|
222
|
+
self.chain = []
|
|
223
|
+
else:
|
|
224
|
+
self.chain = chain
|
|
225
|
+
if plugin is None:
|
|
226
|
+
self.plugin = {}
|
|
227
|
+
else:
|
|
228
|
+
self.plugin = plugin
|
|
229
|
+
|
|
230
|
+
def is_enable(self):
|
|
231
|
+
return bool(self.enable)
|
|
232
|
+
|
|
233
|
+
def get_chain(self):
|
|
234
|
+
return self.chain
|
|
235
|
+
|
|
236
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
237
|
+
return load_plugin_config("statReporter", plugin_name, self.plugin, value)
|
|
238
|
+
|
|
239
|
+
def get_prometheus(self):
|
|
240
|
+
"""Return the typed Prometheus options from YAML or code."""
|
|
241
|
+
result = prometheus_config(self.plugin.get("prometheus", {}))
|
|
242
|
+
self.plugin["prometheus"] = result
|
|
243
|
+
return result
|
|
244
|
+
|
|
245
|
+
def verify(self):
|
|
246
|
+
if type(self.enable) is not bool or not isinstance(self.chain, list):
|
|
247
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "Invalid global.statReporter enable/chain")
|
|
248
|
+
if not isinstance(self.plugin, dict):
|
|
249
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "statReporter.plugin must be an object")
|
|
250
|
+
if self.enable and (len(self.chain) != 1 or self.chain[0] not in ("prometheus", "pushgateway")):
|
|
251
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
252
|
+
"global.statReporter.chain must be [prometheus] or [pushgateway]")
|
|
253
|
+
if self.enable and self.chain == ["pushgateway"] and self.get_prometheus().type != "push":
|
|
254
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
255
|
+
"statReporter chain [pushgateway] requires prometheus.type=push")
|
|
256
|
+
self.get_prometheus()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def set_default(self):
|
|
260
|
+
if self.enable is None:
|
|
261
|
+
self.enable = DefaultConf.DefaultStatReportEnabled
|
|
262
|
+
if len(self.chain) == 0:
|
|
263
|
+
self.chain = [DefaultConf.DefaultStatReporter]
|
|
264
|
+
self.get_prometheus()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class EventReporterConfig:
|
|
268
|
+
"""Configure the context-owned event reporter chain."""
|
|
269
|
+
|
|
270
|
+
def __init__(self, enable: bool | None = None, chain: List[str] = None, plugin: Dict = None):
|
|
271
|
+
self.enable = enable
|
|
272
|
+
self.chain = [] if chain is None else chain
|
|
273
|
+
self.plugin = {} if plugin is None else plugin
|
|
274
|
+
|
|
275
|
+
def is_enable(self):
|
|
276
|
+
return bool(self.enable)
|
|
277
|
+
|
|
278
|
+
def get_chain(self):
|
|
279
|
+
return self.chain
|
|
280
|
+
|
|
281
|
+
def get_pushgateway(self):
|
|
282
|
+
"""Return the typed Pushgateway event options from YAML or code."""
|
|
283
|
+
result = pushgateway_event_config(self.plugin.get("pushgateway", {}))
|
|
284
|
+
self.plugin["pushgateway"] = result
|
|
285
|
+
return result
|
|
286
|
+
|
|
287
|
+
def verify(self):
|
|
288
|
+
if type(self.enable) is not bool or not isinstance(self.chain, list):
|
|
289
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "Invalid global.eventReporter enable/chain")
|
|
290
|
+
if not isinstance(self.plugin, dict):
|
|
291
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "eventReporter.plugin must be an object")
|
|
292
|
+
if self.enable and self.chain != ["pushgateway"]:
|
|
293
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "Only the pushgateway event reporter is supported")
|
|
294
|
+
self.get_pushgateway()
|
|
295
|
+
|
|
296
|
+
def set_default(self):
|
|
297
|
+
if self.enable is None:
|
|
298
|
+
self.enable = False
|
|
299
|
+
if len(self.chain) == 0:
|
|
300
|
+
self.chain = ["pushgateway"]
|
|
301
|
+
self.get_pushgateway()
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# 本地缓存配置
|
|
305
|
+
class LocalCacheConfig:
|
|
306
|
+
def __init__(self, service_expire_time: int | None = None, service_refresh_interval: int | None = None, persist_dir: str = "",
|
|
307
|
+
type: str = "",
|
|
308
|
+
persist_max_write_retry: int = 0,
|
|
309
|
+
persist_max_read_retry: int = 0, persist_retry_interval: int | None = None, plugin: Dict = None):
|
|
310
|
+
# // consumer.localCache.service.expireTime,
|
|
311
|
+
# // 服务的超时淘汰时间
|
|
312
|
+
# ServiceExpireTime *time.Duration `yaml:"serviceExpireTime"`
|
|
313
|
+
# // consumer.localCache.service.refreshInterval
|
|
314
|
+
# // 服务的定期刷新时间
|
|
315
|
+
# ServiceRefreshInterval *time.Duration `yaml:"serviceRefreshInterval"`
|
|
316
|
+
# // consumer.localCache.persistDir
|
|
317
|
+
# // 本地缓存持久化路径
|
|
318
|
+
# PersistDir string `yaml:"persistDir"`
|
|
319
|
+
# // consumer.localCache.type
|
|
320
|
+
# // 本地缓存类型,默认inmemory,可修改成具体的缓存插件名
|
|
321
|
+
# Type string `yaml:"type"`
|
|
322
|
+
# // consumer.localCache.persistMaxWriteRetry
|
|
323
|
+
# PersistMaxWriteRetry int `yaml:"persistMaxWriteRetry"`
|
|
324
|
+
# // consumer.localCache.persistReadRetry
|
|
325
|
+
# PersistMaxReadRetry int `yaml:"persistMaxReadRetry"`
|
|
326
|
+
# // consumer.localCache.persistRetryInterval
|
|
327
|
+
# PersistRetryInterval *time.Duration `yaml:"persistRetryInterval"`
|
|
328
|
+
# // 插件相关配置
|
|
329
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
330
|
+
self.service_expire_time = service_expire_time
|
|
331
|
+
self.service_refresh_interval = service_refresh_interval
|
|
332
|
+
self.persist_dir = persist_dir
|
|
333
|
+
self.type = type
|
|
334
|
+
self.persist_max_write_retry = persist_max_write_retry
|
|
335
|
+
self.persist_max_read_retry = persist_max_read_retry
|
|
336
|
+
self.persist_retry_interval = persist_retry_interval
|
|
337
|
+
if plugin is None:
|
|
338
|
+
self.plugin = {}
|
|
339
|
+
else:
|
|
340
|
+
self.plugin = plugin
|
|
341
|
+
|
|
342
|
+
# GetServiceExpireTime consumer.localCache.service.expireTime,
|
|
343
|
+
# 服务的超时淘汰时间
|
|
344
|
+
def get_service_expire_time(self):
|
|
345
|
+
return self.service_expire_time
|
|
346
|
+
|
|
347
|
+
# GetServiceRefreshInterval consumer.localCache.service.refreshInterval
|
|
348
|
+
# 服务的定期刷新时间
|
|
349
|
+
def get_service_refresh_interval(self):
|
|
350
|
+
return self.service_refresh_interval
|
|
351
|
+
|
|
352
|
+
# GetPersistPath consumer.localCache.persist.path
|
|
353
|
+
# 本地缓存持久化路径
|
|
354
|
+
def get_persist_dir(self):
|
|
355
|
+
return self.persist_dir
|
|
356
|
+
|
|
357
|
+
def get_type(self):
|
|
358
|
+
return self.type
|
|
359
|
+
|
|
360
|
+
def get_persist_max_write_retry(self):
|
|
361
|
+
return self.persist_max_write_retry
|
|
362
|
+
|
|
363
|
+
def get_persist_max_read_retry(self):
|
|
364
|
+
return self.persist_max_read_retry
|
|
365
|
+
|
|
366
|
+
def get_persist_retry_interval(self):
|
|
367
|
+
"""Return the delay between disk retries in milliseconds."""
|
|
368
|
+
return self.persist_retry_interval
|
|
369
|
+
|
|
370
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
371
|
+
return load_plugin_config("localCache", plugin_name, self.plugin, value)
|
|
372
|
+
|
|
373
|
+
def verify(self):
|
|
374
|
+
if self.service_expire_time < DefaultConf.DefaultMinServiceExpireTime:
|
|
375
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
376
|
+
"LocalCacheConfig: consumer.localCache.serviceExpireTime %d is less than the minimal "
|
|
377
|
+
"allowed duration %d" % (self.service_expire_time, DefaultConf.DefaultMinServiceExpireTime))
|
|
378
|
+
for name in ("persist_max_write_retry", "persist_max_read_retry", "persist_retry_interval"):
|
|
379
|
+
value = getattr(self, name)
|
|
380
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
381
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
382
|
+
"LocalCacheConfig: %s must be non-negative integer" % name)
|
|
383
|
+
|
|
384
|
+
def set_default(self):
|
|
385
|
+
if self.service_expire_time is None:
|
|
386
|
+
self.service_expire_time = DefaultConf.DefaultServiceExpireTime
|
|
387
|
+
if self.service_refresh_interval is None:
|
|
388
|
+
self.service_refresh_interval = DefaultConf.DefaultServiceRefreshIntervalDuration
|
|
389
|
+
if len(self.persist_dir) == 0:
|
|
390
|
+
self.persist_dir = DefaultConf.DefaultCachePersistDir
|
|
391
|
+
if len(self.type) == 0:
|
|
392
|
+
self.type = DefaultConf.DefaultLocalCache
|
|
393
|
+
if self.persist_retry_interval is None:
|
|
394
|
+
self.persist_retry_interval = DefaultConf.DefaultPersistRetryInterval
|
|
395
|
+
if self.persist_max_read_retry == 0:
|
|
396
|
+
self.persist_max_read_retry = DefaultConf.DefaultPersistMaxReadRetry
|
|
397
|
+
if self.persist_max_write_retry == 0:
|
|
398
|
+
self.persist_max_write_retry = DefaultConf.DefaultPersistMaxWriteRetry
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# 服务路由配置
|
|
402
|
+
class ServiceRouterConfig:
|
|
403
|
+
def __init__(self, before_chain: List[str] = None, chain: List[str] = None,
|
|
404
|
+
after_chain: List[str] = None, plugin: Dict = None, percent_of_min_instances: float = 0):
|
|
405
|
+
# //服务路由责任链
|
|
406
|
+
# Chain []string `yaml:"chain"`
|
|
407
|
+
# // 插件相关配置
|
|
408
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
409
|
+
# // 进行过滤时的最大过滤比例
|
|
410
|
+
# PercentOfMinInstances *float64 `yaml:"percentOfMinInstances"`
|
|
411
|
+
self.before_chain = [] if before_chain is None else before_chain
|
|
412
|
+
self.chain = [] if chain is None else chain
|
|
413
|
+
self.after_chain = [] if after_chain is None else after_chain
|
|
414
|
+
self.percent_of_min_instances = percent_of_min_instances
|
|
415
|
+
if plugin is None:
|
|
416
|
+
self.plugin = {}
|
|
417
|
+
else:
|
|
418
|
+
self.plugin = plugin
|
|
419
|
+
|
|
420
|
+
def get_chain(self):
|
|
421
|
+
return self.chain
|
|
422
|
+
|
|
423
|
+
def get_before_chain(self):
|
|
424
|
+
return self.before_chain
|
|
425
|
+
|
|
426
|
+
def get_after_chain(self):
|
|
427
|
+
return self.after_chain
|
|
428
|
+
|
|
429
|
+
def get_percent_of_min_instances(self):
|
|
430
|
+
return self.percent_of_min_instances
|
|
431
|
+
|
|
432
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
433
|
+
return load_plugin_config("serviceRouter", plugin_name, self.plugin, value)
|
|
434
|
+
|
|
435
|
+
def verify(self):
|
|
436
|
+
if self.percent_of_min_instances >= 1 or self.percent_of_min_instances < 0:
|
|
437
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
438
|
+
"ServiceRouterConfig: consumer.servicerouter.percentOfMinInstances "
|
|
439
|
+
"must be in range [0.0, 1.0)")
|
|
440
|
+
for name in ("before_chain", "chain", "after_chain"):
|
|
441
|
+
value = getattr(self, name)
|
|
442
|
+
if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
|
|
443
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
444
|
+
"ServiceRouterConfig: %s must be a list of router names" % name)
|
|
445
|
+
if len(self.before_chain) == 0 or len(self.after_chain) == 0:
|
|
446
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
447
|
+
"ServiceRouterConfig: beforeChain and afterChain cannot be empty")
|
|
448
|
+
|
|
449
|
+
def set_default(self):
|
|
450
|
+
if len(self.before_chain) == 0:
|
|
451
|
+
self.before_chain = [DefaultConf.DefaultServiceRouterIsolated]
|
|
452
|
+
if len(self.chain) == 0:
|
|
453
|
+
self.chain = [DefaultConf.DefaultServiceRouterMetadata]
|
|
454
|
+
if len(self.after_chain) == 0:
|
|
455
|
+
self.after_chain = [DefaultConf.DefaultServiceRouterRecover]
|
|
456
|
+
if not self.percent_of_min_instances:
|
|
457
|
+
self.percent_of_min_instances = DefaultConf.DefaultPercentOfMinInstances
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
# 通用加载插件配置的方法,主要是进行一次序列化和反序列化的过程
|
|
461
|
+
# 一般只会在插件的Init过程调用
|
|
462
|
+
def load_plugin_config(typ: str, plugin_name: str, configs: Dict, value):
|
|
463
|
+
if configs is None:
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
if plugin_name not in configs:
|
|
467
|
+
return None
|
|
468
|
+
|
|
469
|
+
config_value = configs[plugin_name]
|
|
470
|
+
# TODO
|
|
471
|
+
# buf, err := yaml.Marshal(configValue)
|
|
472
|
+
# if nil != err {
|
|
473
|
+
# return model.NewSDKError(model.ErrCodeAPIInvalidConfig,
|
|
474
|
+
# err, "fail to marshal plugin %s config", typ+":"+pluginName)
|
|
475
|
+
# }
|
|
476
|
+
# err = yaml.Unmarshal(buf, value)
|
|
477
|
+
# if nil != err {
|
|
478
|
+
# return model.NewSDKError(model.ErrCodeAPIInvalidConfig,
|
|
479
|
+
# err, "fail to unmarshal plugin %s config to type %v", typ+":"+pluginName,
|
|
480
|
+
# reflect.ValueOf(value))
|
|
481
|
+
# }
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
# 负载均衡配置
|
|
485
|
+
class LoadBalancerConfig:
|
|
486
|
+
def __init__(self, type: str = "", plugin: Dict = None):
|
|
487
|
+
# //负载均衡类型
|
|
488
|
+
# Type string `yaml:"type"`
|
|
489
|
+
# // 插件相关配置
|
|
490
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
491
|
+
self.type = type
|
|
492
|
+
if plugin is None:
|
|
493
|
+
self.plugin = {}
|
|
494
|
+
else:
|
|
495
|
+
self.plugin = plugin
|
|
496
|
+
|
|
497
|
+
def get_type(self):
|
|
498
|
+
return self.type
|
|
499
|
+
|
|
500
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
501
|
+
return load_plugin_config("loadbalancer", plugin_name, self.plugin, value)
|
|
502
|
+
|
|
503
|
+
def verify(self):
|
|
504
|
+
pass
|
|
505
|
+
|
|
506
|
+
def set_default(self):
|
|
507
|
+
if len(self.type) == 0:
|
|
508
|
+
self.type = DefaultConf.DefaultLoadBalancerWR
|
|
509
|
+
elif self.type == "wr":
|
|
510
|
+
self.type = DefaultConf.DefaultLoadBalancerWR
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
# 熔断相关配置
|
|
514
|
+
class CircuitBreakerConfig:
|
|
515
|
+
"""Configure local default instance circuit breaking; durations are milliseconds."""
|
|
516
|
+
|
|
517
|
+
def __init__(self, enable: bool | None = None, check_period: int | None = None, chain: List[str] = None,
|
|
518
|
+
plugin: Dict = None, sleep_window: int | None = None,
|
|
519
|
+
success_count_after_half_open: int | None = None, default_rule_enable: bool | None = None,
|
|
520
|
+
default_error_count: int | None = None, default_error_percent: int | None = None,
|
|
521
|
+
default_interval: int | None = None, default_minimum_request: int | None = None):
|
|
522
|
+
# //是否启动熔断
|
|
523
|
+
# Enable *bool `yaml:"enable"`
|
|
524
|
+
# //熔断器定时检查周期
|
|
525
|
+
# CheckPeriod *time.Duration `yaml:"checkPeriod"`
|
|
526
|
+
# //熔断插件链
|
|
527
|
+
# Chain []string `yaml:"chain"`
|
|
528
|
+
# // 插件相关配置
|
|
529
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
530
|
+
self.enable = enable
|
|
531
|
+
self.check_period = check_period
|
|
532
|
+
self.sleep_window = sleep_window
|
|
533
|
+
self.success_count_after_half_open = success_count_after_half_open
|
|
534
|
+
self.default_rule_enable = default_rule_enable
|
|
535
|
+
self.default_error_count = default_error_count
|
|
536
|
+
self.default_error_percent = default_error_percent
|
|
537
|
+
self.default_interval = default_interval
|
|
538
|
+
self.default_minimum_request = default_minimum_request
|
|
539
|
+
if chain is None:
|
|
540
|
+
self.chain = []
|
|
541
|
+
else:
|
|
542
|
+
self.chain = chain
|
|
543
|
+
if plugin is None:
|
|
544
|
+
self.plugin = {}
|
|
545
|
+
else:
|
|
546
|
+
self.plugin = plugin
|
|
547
|
+
|
|
548
|
+
def is_enable(self):
|
|
549
|
+
return bool(self.enable)
|
|
550
|
+
|
|
551
|
+
def get_chain(self):
|
|
552
|
+
return self.chain
|
|
553
|
+
|
|
554
|
+
def get_check_period(self):
|
|
555
|
+
return self.check_period
|
|
556
|
+
|
|
557
|
+
def get_sleep_window(self) -> int:
|
|
558
|
+
"""Return the open-to-half-open delay in milliseconds."""
|
|
559
|
+
return self.sleep_window
|
|
560
|
+
|
|
561
|
+
def get_success_count_after_half_open(self) -> int:
|
|
562
|
+
"""Return the recovery success threshold and half-open permit count."""
|
|
563
|
+
return self.success_count_after_half_open
|
|
564
|
+
|
|
565
|
+
def is_default_rule_enable(self) -> bool:
|
|
566
|
+
"""Return whether the local default instance rule is enabled."""
|
|
567
|
+
return bool(self.default_rule_enable)
|
|
568
|
+
|
|
569
|
+
def get_default_error_count(self) -> int:
|
|
570
|
+
"""Return the consecutive failure threshold."""
|
|
571
|
+
return self.default_error_count
|
|
572
|
+
|
|
573
|
+
def get_default_error_percent(self) -> int:
|
|
574
|
+
"""Return the error-rate threshold as an integer percentage."""
|
|
575
|
+
return self.default_error_percent
|
|
576
|
+
|
|
577
|
+
def get_default_interval(self) -> int:
|
|
578
|
+
"""Return the error-rate window and delayed check interval in milliseconds."""
|
|
579
|
+
return self.default_interval
|
|
580
|
+
|
|
581
|
+
def get_default_minimum_request(self) -> int:
|
|
582
|
+
"""Return the minimum sample count for an error-rate check."""
|
|
583
|
+
return self.default_minimum_request
|
|
584
|
+
|
|
585
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
586
|
+
return load_plugin_config("circuitbreaker", plugin_name, self.plugin, value)
|
|
587
|
+
|
|
588
|
+
def verify(self):
|
|
589
|
+
for name in ("enable", "default_rule_enable"):
|
|
590
|
+
if not isinstance(getattr(self, name), bool):
|
|
591
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
592
|
+
"CircuitBreakerConfig: %s must be boolean" % name)
|
|
593
|
+
for name, minimum in (
|
|
594
|
+
("check_period", DefaultConf.MinCircuitBreakerCheckPeriod),
|
|
595
|
+
("sleep_window", DefaultConf.MinSleepWindow),
|
|
596
|
+
("success_count_after_half_open", 1),
|
|
597
|
+
("default_error_count", 1),
|
|
598
|
+
("default_error_percent", 0),
|
|
599
|
+
("default_interval", 1),
|
|
600
|
+
("default_minimum_request", 1),
|
|
601
|
+
):
|
|
602
|
+
value = getattr(self, name)
|
|
603
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
|
|
604
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
605
|
+
"CircuitBreakerConfig: %s must be integer >= %d" % (name, minimum))
|
|
606
|
+
if self.default_error_percent > 100:
|
|
607
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
608
|
+
"CircuitBreakerConfig: default_error_percent must be in range [0, 100]")
|
|
609
|
+
|
|
610
|
+
def set_default(self):
|
|
611
|
+
defaults = {
|
|
612
|
+
"enable": DefaultConf.DefaultCircuitBreakerEnabled,
|
|
613
|
+
"check_period": DefaultConf.DefaultCircuitBreakerCheckPeriod,
|
|
614
|
+
"sleep_window": DefaultConf.DefaultSleepWindow,
|
|
615
|
+
"success_count_after_half_open": DefaultConf.DefaultSuccessCountAfterHalfOpen,
|
|
616
|
+
"default_rule_enable": DefaultConf.DefaultRuleEnable,
|
|
617
|
+
"default_error_count": DefaultConf.DefaultErrorCount,
|
|
618
|
+
"default_error_percent": DefaultConf.DefaultErrorPercent,
|
|
619
|
+
"default_interval": DefaultConf.DefaultInterval,
|
|
620
|
+
"default_minimum_request": DefaultConf.DefaultMinimumRequest,
|
|
621
|
+
}
|
|
622
|
+
for name, value in defaults.items():
|
|
623
|
+
if getattr(self, name) is None:
|
|
624
|
+
setattr(self, name, value)
|
|
625
|
+
if type(self.success_count_after_half_open) is int and self.success_count_after_half_open == 0:
|
|
626
|
+
self.success_count_after_half_open = DefaultConf.DefaultSuccessCountAfterHalfOpen
|
|
627
|
+
if len(self.chain) == 0:
|
|
628
|
+
self.chain = [DefaultConf.DefaultCircuitBreaker]
|
|
629
|
+
else:
|
|
630
|
+
self.chain = [DefaultConf.DefaultCircuitBreaker if name == "err_count" else name for name in self.chain]
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
# 消费者配置
|
|
634
|
+
class ConsumerConfig(SelfVerifier):
|
|
635
|
+
def __init__(self, local_cache: LocalCacheConfig = None, service_router: ServiceRouterConfig = None,
|
|
636
|
+
load_balance: LoadBalancerConfig = None,
|
|
637
|
+
circuit_breaker: CircuitBreakerConfig = None,
|
|
638
|
+
weight_adjust: "WeightAdjustConfig" = None):
|
|
639
|
+
# LocalCache *LocalCacheConfigImpl `yaml:"localCache"`
|
|
640
|
+
# ServiceRouter *ServiceRouterConfigImpl `yaml:"serviceRouter"`
|
|
641
|
+
# Loadbalancer *LoadBalancerConfigImpl `yaml:"loadbalancer"`
|
|
642
|
+
# CircuitBreaker *CircuitBreakerConfigImpl `yaml:"circuitbreaker"`
|
|
643
|
+
self.local_cache = local_cache
|
|
644
|
+
self.service_router = service_router
|
|
645
|
+
self.load_balance = load_balance
|
|
646
|
+
self.circuit_breaker = circuit_breaker
|
|
647
|
+
self.weight_adjust = weight_adjust
|
|
648
|
+
|
|
649
|
+
def get_service_router(self) -> ServiceRouterConfig:
|
|
650
|
+
return self.service_router
|
|
651
|
+
|
|
652
|
+
def get_load_balancer(self) -> LoadBalancerConfig:
|
|
653
|
+
return self.load_balance
|
|
654
|
+
|
|
655
|
+
def get_circuit_breaker(self) -> CircuitBreakerConfig:
|
|
656
|
+
return self.circuit_breaker
|
|
657
|
+
|
|
658
|
+
def get_local_cache(self) -> LocalCacheConfig:
|
|
659
|
+
return self.local_cache
|
|
660
|
+
|
|
661
|
+
def get_weight_adjust(self) -> "WeightAdjustConfig":
|
|
662
|
+
return self.weight_adjust
|
|
663
|
+
|
|
664
|
+
def verify(self):
|
|
665
|
+
try:
|
|
666
|
+
self.local_cache.verify()
|
|
667
|
+
except SDKError as err:
|
|
668
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
669
|
+
"ConsumerConfig: local_cache check err: %s" % err.msg)
|
|
670
|
+
try:
|
|
671
|
+
self.service_router.verify()
|
|
672
|
+
except SDKError as err:
|
|
673
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
674
|
+
"ConsumerConfig: service_router check err: %s" % err.msg)
|
|
675
|
+
try:
|
|
676
|
+
self.load_balance.verify()
|
|
677
|
+
except SDKError as err:
|
|
678
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
679
|
+
"ConsumerConfig: load_balance check err: %s" % err.msg)
|
|
680
|
+
self.circuit_breaker.verify()
|
|
681
|
+
if self.weight_adjust is not None:
|
|
682
|
+
self.weight_adjust.verify()
|
|
683
|
+
|
|
684
|
+
def set_default(self):
|
|
685
|
+
if not self.local_cache:
|
|
686
|
+
self.local_cache = LocalCacheConfig()
|
|
687
|
+
if not self.service_router:
|
|
688
|
+
self.service_router = ServiceRouterConfig()
|
|
689
|
+
if not self.load_balance:
|
|
690
|
+
self.load_balance = LoadBalancerConfig()
|
|
691
|
+
if not self.circuit_breaker:
|
|
692
|
+
self.circuit_breaker = CircuitBreakerConfig()
|
|
693
|
+
if not self.weight_adjust:
|
|
694
|
+
self.weight_adjust = WeightAdjustConfig()
|
|
695
|
+
self.load_balance.set_default()
|
|
696
|
+
self.service_router.set_default()
|
|
697
|
+
self.local_cache.set_default()
|
|
698
|
+
self.service_router.set_default()
|
|
699
|
+
self.circuit_breaker.set_default()
|
|
700
|
+
self.weight_adjust.set_default()
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class WeightAdjustConfig(SelfVerifier):
|
|
704
|
+
"""Configure dynamic instance weight adjusters such as warmup."""
|
|
705
|
+
|
|
706
|
+
def __init__(self, enable: bool | None = None, chain: List[str] = None):
|
|
707
|
+
self.enable = enable
|
|
708
|
+
self.chain = chain
|
|
709
|
+
|
|
710
|
+
def is_enable(self) -> bool:
|
|
711
|
+
return self.enable
|
|
712
|
+
|
|
713
|
+
def get_chain(self) -> List[str]:
|
|
714
|
+
return self.chain
|
|
715
|
+
|
|
716
|
+
def set_default(self):
|
|
717
|
+
if self.enable is None:
|
|
718
|
+
self.enable = True
|
|
719
|
+
if self.chain is None:
|
|
720
|
+
self.chain = []
|
|
721
|
+
|
|
722
|
+
def verify(self):
|
|
723
|
+
if not isinstance(self.enable, bool):
|
|
724
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
725
|
+
"ConsumerConfig: weight_adjust.enable must be a boolean")
|
|
726
|
+
if (not isinstance(self.chain, list)
|
|
727
|
+
or any(not isinstance(name, str) or not name for name in self.chain)):
|
|
728
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
729
|
+
"ConsumerConfig: weight_adjust.chain must be a list of plugin names")
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
# 对接注册中心相关配置
|
|
733
|
+
class ServerConnectorConfig:
|
|
734
|
+
def __init__(self, addresses=None, protocol="", connect_timeout=None, message_time=None,
|
|
735
|
+
connection_idle_timeout: int | None = None, request_queue_size: int | None = None,
|
|
736
|
+
server_switch_interval: int | None = None, plugin: Dict = None, token: str = "",
|
|
737
|
+
trusted_ca_file: str = "", cert_file: str = "", key_file: str = ""):
|
|
738
|
+
# Addresses []string `yaml:"addresses"`
|
|
739
|
+
# Protocol string `yaml:"protocol"`
|
|
740
|
+
# ConnectTimeout *time.Duration `yaml:"connectTimeout"`
|
|
741
|
+
# //远程请求超时时间
|
|
742
|
+
# MessageTimeout *time.Duration `yaml:"messageTimeout"`
|
|
743
|
+
# ConnectionIdleTimeout *time.Duration `yaml:"connectionIdleTimeout"`
|
|
744
|
+
# RequestQueueSize *int32 `yaml:"requestQueueSize"`
|
|
745
|
+
# ServerSwitchInterval *time.Duration `yaml:"serverSwitchInterval"`
|
|
746
|
+
# Plugin map[string]map[string]interface{} `yaml:"plugin"`
|
|
747
|
+
self.addresses = addresses
|
|
748
|
+
self.protocol = protocol
|
|
749
|
+
self.connect_timeout = connect_timeout
|
|
750
|
+
self.message_time = message_time
|
|
751
|
+
self.connection_idle_timeout = connection_idle_timeout
|
|
752
|
+
self.request_queue_size = request_queue_size
|
|
753
|
+
self.server_switch_interval = server_switch_interval
|
|
754
|
+
self.token = token
|
|
755
|
+
self.trusted_ca_file = trusted_ca_file
|
|
756
|
+
self.cert_file = cert_file
|
|
757
|
+
self.key_file = key_file
|
|
758
|
+
if plugin is None:
|
|
759
|
+
self.plugin = {}
|
|
760
|
+
else:
|
|
761
|
+
self.plugin = plugin
|
|
762
|
+
|
|
763
|
+
def get_protocol(self):
|
|
764
|
+
return self.protocol
|
|
765
|
+
|
|
766
|
+
def get_addresses(self):
|
|
767
|
+
return self.addresses
|
|
768
|
+
|
|
769
|
+
def get_token(self):
|
|
770
|
+
return self.token
|
|
771
|
+
|
|
772
|
+
def get_connect_timeout(self):
|
|
773
|
+
return self.connect_timeout
|
|
774
|
+
|
|
775
|
+
def get_message_timeout(self):
|
|
776
|
+
return self.message_time
|
|
777
|
+
|
|
778
|
+
def get_request_queue_size(self):
|
|
779
|
+
return self.request_queue_size
|
|
780
|
+
|
|
781
|
+
def get_server_switch_interval(self):
|
|
782
|
+
return self.server_switch_interval
|
|
783
|
+
|
|
784
|
+
def get_connect_idle_timeout(self):
|
|
785
|
+
return self.connection_idle_timeout
|
|
786
|
+
|
|
787
|
+
def get_plugin_config(self, plugin_name: str, value):
|
|
788
|
+
return load_plugin_config("registry", plugin_name, self.plugin, value)
|
|
789
|
+
|
|
790
|
+
def verify(self):
|
|
791
|
+
for name in ("trusted_ca_file", "cert_file", "key_file"):
|
|
792
|
+
if not isinstance(getattr(self, name), str):
|
|
793
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, f"serverConnector.{name} must be a string")
|
|
794
|
+
if bool(self.cert_file) != bool(self.key_file):
|
|
795
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "TLS certFile and keyFile must be paired")
|
|
796
|
+
if len(self.addresses) == 0:
|
|
797
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
798
|
+
"ServerConnectorConfig: global.serverConnector.addresses is empty")
|
|
799
|
+
if self.request_queue_size < 0:
|
|
800
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
801
|
+
"ServerConnectorConfig: global.serverConnector.requestQueueSize %d is invalid" %
|
|
802
|
+
self.request_queue_size)
|
|
803
|
+
if self.connection_idle_timeout < DefaultConf.DefaultMinTimingInterval:
|
|
804
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
805
|
+
"ServerConnectorConfig: global.serverConnector.connectionIdleTimeout %d is less than "
|
|
806
|
+
"minimal timing interval %d" % (
|
|
807
|
+
self.connection_idle_timeout, DefaultConf.DefaultMinTimingInterval))
|
|
808
|
+
if self.server_switch_interval <= self.connection_idle_timeout:
|
|
809
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
810
|
+
"ServerConnectorConfig: global.serverConnector.serverSwitchInterval %d is "
|
|
811
|
+
"less than or equal to global. serverConnector.connectionIdleTimeout %d"
|
|
812
|
+
% (self.connection_idle_timeout, DefaultConf.DefaultMinTimingInterval))
|
|
813
|
+
|
|
814
|
+
def set_default(self):
|
|
815
|
+
if not self.addresses:
|
|
816
|
+
self.addresses = [ip_from_int_with_port(ipstr) for ipstr in DefaultConf.DefaultBuiltInServer]
|
|
817
|
+
if self.connect_timeout is None:
|
|
818
|
+
self.connect_timeout = DefaultConf.DefaultServerConnectTimeout
|
|
819
|
+
if self.message_time is None:
|
|
820
|
+
self.message_time = DefaultConf.DefaultServerMessageTimeout
|
|
821
|
+
if self.connection_idle_timeout is None:
|
|
822
|
+
self.connection_idle_timeout = DefaultConf.DefaultServerConnectionIdleTimeout
|
|
823
|
+
if self.request_queue_size is None:
|
|
824
|
+
self.request_queue_size = DefaultConf.DefaultRequestQueueSize
|
|
825
|
+
if self.server_switch_interval is None:
|
|
826
|
+
self.server_switch_interval = DefaultConf.DefaultServerSwitchInterval
|
|
827
|
+
if len(self.protocol) == 0:
|
|
828
|
+
self.protocol = DefaultConf.DefaultServerConnector
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def ip_from_int_with_port(ipstr):
|
|
832
|
+
ip_parts = ipstr.split(":")
|
|
833
|
+
try:
|
|
834
|
+
host = str(ipaddress.IPv4Address(int(ip_parts[0])))
|
|
835
|
+
except ValueError:
|
|
836
|
+
host = ip_parts[0]
|
|
837
|
+
return "%s:%s" % (host, ip_parts[1])
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
class GlobalConfig(SelfVerifier):
|
|
841
|
+
def __init__(self, system_conf: SystemConfig = None, api_conf: APIConfig = None,
|
|
842
|
+
server_connector: ServerConnectorConfig = None, stat_reporter: StatReporterConfig = None,
|
|
843
|
+
admin: AdminConfig = None, event_reporter: EventReporterConfig = None):
|
|
844
|
+
# System *SystemConfigImpl `yaml:"system"`
|
|
845
|
+
# API *APIConfigImpl `yaml:"api"`
|
|
846
|
+
# ServerConnector *ServerConnectorConfigImpl `yaml:"serverConnector"`
|
|
847
|
+
# StatReporter *StatReporterConfigImpl `yaml:"statReporter"`
|
|
848
|
+
self.system_conf = system_conf
|
|
849
|
+
self.api_conf = api_conf
|
|
850
|
+
self.server_connector = server_connector
|
|
851
|
+
self.stat_reporter = stat_reporter
|
|
852
|
+
self.admin = admin
|
|
853
|
+
self.event_reporter = event_reporter
|
|
854
|
+
|
|
855
|
+
def get_admin(self):
|
|
856
|
+
"""Return the context's metrics listener configuration."""
|
|
857
|
+
return self.admin
|
|
858
|
+
|
|
859
|
+
# 获取系统配置
|
|
860
|
+
def get_system(self) -> SystemConfig:
|
|
861
|
+
return self.system_conf
|
|
862
|
+
|
|
863
|
+
# global.api前缀开头的所有配置项
|
|
864
|
+
def get_api(self) -> APIConfig:
|
|
865
|
+
return self.api_conf
|
|
866
|
+
|
|
867
|
+
# global.registry前缀开头的所有配置项
|
|
868
|
+
def get_server_connector(self) -> ServerConnectorConfig:
|
|
869
|
+
return self.server_connector
|
|
870
|
+
|
|
871
|
+
# cl5.global.statReporter前缀开头的所有配置项
|
|
872
|
+
def get_stat_reporter(self) -> StatReporterConfig:
|
|
873
|
+
return self.stat_reporter
|
|
874
|
+
|
|
875
|
+
def get_event_reporter(self) -> EventReporterConfig:
|
|
876
|
+
"""Return the event reporter chain configuration."""
|
|
877
|
+
return self.event_reporter
|
|
878
|
+
|
|
879
|
+
def verify(self):
|
|
880
|
+
self.admin.verify()
|
|
881
|
+
self.stat_reporter.verify()
|
|
882
|
+
self.event_reporter.verify()
|
|
883
|
+
try:
|
|
884
|
+
self.system_conf.verify()
|
|
885
|
+
except SDKError as err:
|
|
886
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
887
|
+
"GlobalConfig: system_conf check err: %s" % err.msg)
|
|
888
|
+
try:
|
|
889
|
+
self.api_conf.verify()
|
|
890
|
+
except SDKError as err:
|
|
891
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
892
|
+
"GlobalConfig: api_conf check err: %s" % err.msg)
|
|
893
|
+
try:
|
|
894
|
+
self.server_connector.verify()
|
|
895
|
+
except SDKError as err:
|
|
896
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
897
|
+
"GlobalConfig: server_connector check err: %s" % err.msg)
|
|
898
|
+
|
|
899
|
+
def set_default(self):
|
|
900
|
+
if self.admin is None:
|
|
901
|
+
self.admin = AdminConfig()
|
|
902
|
+
self.admin.set_default()
|
|
903
|
+
if not self.api_conf:
|
|
904
|
+
self.api_conf = APIConfig()
|
|
905
|
+
if not self.system_conf:
|
|
906
|
+
self.system_conf = SystemConfig()
|
|
907
|
+
if not self.server_connector:
|
|
908
|
+
self.server_connector = ServerConnectorConfig()
|
|
909
|
+
if not self.stat_reporter:
|
|
910
|
+
self.stat_reporter = StatReporterConfig()
|
|
911
|
+
if not self.event_reporter:
|
|
912
|
+
self.event_reporter = EventReporterConfig()
|
|
913
|
+
self.api_conf.set_default()
|
|
914
|
+
self.system_conf.set_default()
|
|
915
|
+
self.server_connector.set_default()
|
|
916
|
+
self.stat_reporter.set_default()
|
|
917
|
+
self.event_reporter.set_default()
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
class LosslessConfig(SelfVerifier):
|
|
921
|
+
"""Configure lossless online/offline behaviour, aligned with the Java SDK."""
|
|
922
|
+
|
|
923
|
+
DELAY_BY_TIME = "DELAY_BY_TIME"
|
|
924
|
+
DELAY_BY_HEALTH_CHECK = "DELAY_BY_HEALTH_CHECK"
|
|
925
|
+
STRATEGIES = (DELAY_BY_TIME, DELAY_BY_HEALTH_CHECK)
|
|
926
|
+
|
|
927
|
+
def __init__(self, enable: bool | None = None, strategy: str | None = None,
|
|
928
|
+
delay_register_interval: int | None = None, health_check_interval: int | None = None,
|
|
929
|
+
health_check_protocol: str = "http", health_check_path: str = "",
|
|
930
|
+
health_check_method: str = "GET"):
|
|
931
|
+
self.enable = enable
|
|
932
|
+
self.strategy = strategy
|
|
933
|
+
self.delay_register_interval = delay_register_interval
|
|
934
|
+
self.health_check_interval = health_check_interval
|
|
935
|
+
self.health_check_protocol = health_check_protocol
|
|
936
|
+
self.health_check_path = health_check_path
|
|
937
|
+
self.health_check_method = health_check_method
|
|
938
|
+
|
|
939
|
+
def is_enable(self) -> bool:
|
|
940
|
+
return self.enable
|
|
941
|
+
|
|
942
|
+
def get_strategy(self) -> str:
|
|
943
|
+
return self.strategy
|
|
944
|
+
|
|
945
|
+
def get_delay_register_interval(self) -> int:
|
|
946
|
+
return self.delay_register_interval
|
|
947
|
+
|
|
948
|
+
def get_health_check_interval(self) -> int:
|
|
949
|
+
return self.health_check_interval
|
|
950
|
+
|
|
951
|
+
def get_health_check_protocol(self) -> str:
|
|
952
|
+
return self.health_check_protocol
|
|
953
|
+
|
|
954
|
+
def get_health_check_path(self) -> str:
|
|
955
|
+
return self.health_check_path
|
|
956
|
+
|
|
957
|
+
def get_health_check_method(self) -> str:
|
|
958
|
+
return self.health_check_method
|
|
959
|
+
|
|
960
|
+
def set_default(self):
|
|
961
|
+
if self.enable is None:
|
|
962
|
+
self.enable = False
|
|
963
|
+
if self.strategy is None:
|
|
964
|
+
self.strategy = self.DELAY_BY_TIME
|
|
965
|
+
if self.delay_register_interval is None:
|
|
966
|
+
self.delay_register_interval = 30000
|
|
967
|
+
if self.health_check_interval is None:
|
|
968
|
+
self.health_check_interval = 5000
|
|
969
|
+
|
|
970
|
+
def verify(self):
|
|
971
|
+
if not isinstance(self.enable, bool):
|
|
972
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
973
|
+
"ProviderConfig: lossless.enable must be a boolean")
|
|
974
|
+
if self.strategy not in self.STRATEGIES:
|
|
975
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
976
|
+
"ProviderConfig: lossless.strategy must be one of %s" % ", ".join(self.STRATEGIES))
|
|
977
|
+
for name in ("delay_register_interval", "health_check_interval"):
|
|
978
|
+
value = getattr(self, name)
|
|
979
|
+
if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
|
|
980
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
981
|
+
f"ProviderConfig: lossless.{name} must be positive integer milliseconds")
|
|
982
|
+
for name in ("health_check_protocol", "health_check_path", "health_check_method"):
|
|
983
|
+
if not isinstance(getattr(self, name), str):
|
|
984
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
985
|
+
f"ProviderConfig: lossless.{name} must be a string")
|
|
986
|
+
if self.health_check_protocol != "http":
|
|
987
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
988
|
+
"ProviderConfig: lossless.healthCheckProtocol only supports http")
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
class ProviderConfig(SelfVerifier):
|
|
992
|
+
"""Configure provider registration, re-registration and lossless lifecycle."""
|
|
993
|
+
|
|
994
|
+
def __init__(self, min_register_interval: int | None = None, lossless: LosslessConfig = None):
|
|
995
|
+
self.min_register_interval = min_register_interval
|
|
996
|
+
self.lossless = lossless
|
|
997
|
+
|
|
998
|
+
def get_min_register_interval(self) -> int:
|
|
999
|
+
"""Return the minimum re-registration interval in milliseconds."""
|
|
1000
|
+
return self.min_register_interval
|
|
1001
|
+
|
|
1002
|
+
def get_lossless(self) -> LosslessConfig:
|
|
1003
|
+
"""Return the lossless online/offline configuration."""
|
|
1004
|
+
return self.lossless
|
|
1005
|
+
|
|
1006
|
+
def set_default(self):
|
|
1007
|
+
if (self.min_register_interval is None
|
|
1008
|
+
or type(self.min_register_interval) is int and self.min_register_interval == 0):
|
|
1009
|
+
self.min_register_interval = 30000
|
|
1010
|
+
if not self.lossless:
|
|
1011
|
+
self.lossless = LosslessConfig()
|
|
1012
|
+
self.lossless.set_default()
|
|
1013
|
+
|
|
1014
|
+
def verify(self):
|
|
1015
|
+
if (isinstance(self.min_register_interval, bool)
|
|
1016
|
+
or not isinstance(self.min_register_interval, int) or self.min_register_interval <= 0):
|
|
1017
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig,
|
|
1018
|
+
"ProviderConfig: minRegisterInterval must be positive integer milliseconds")
|
|
1019
|
+
if self.lossless is not None:
|
|
1020
|
+
self.lossless.verify()
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
class SkillConnectorConfig(SelfVerifier):
|
|
1024
|
+
"""Configure the independent Skill connection and persistent fallback."""
|
|
1025
|
+
|
|
1026
|
+
def __init__(self, addresses=None, protocol="grpc", connector_type="polaris", token="",
|
|
1027
|
+
message_timeout=5000, download_timeout=30000, persist_enable=True,
|
|
1028
|
+
persist_dir="./polaris/backup/skill", persist_max_write_retry=1,
|
|
1029
|
+
persist_max_read_retry=0, persist_retry_interval=500, fallback_to_local_cache=True,
|
|
1030
|
+
trusted_ca_file="", cert_file="", key_file=""):
|
|
1031
|
+
self.addresses = addresses
|
|
1032
|
+
self.protocol = protocol
|
|
1033
|
+
self.connector_type = connector_type
|
|
1034
|
+
self.token = token
|
|
1035
|
+
self.message_timeout = message_timeout
|
|
1036
|
+
self.download_timeout = download_timeout
|
|
1037
|
+
self.persist_enable = persist_enable
|
|
1038
|
+
self.persist_dir = persist_dir
|
|
1039
|
+
self.persist_max_write_retry = persist_max_write_retry
|
|
1040
|
+
self.persist_max_read_retry = persist_max_read_retry
|
|
1041
|
+
self.persist_retry_interval = persist_retry_interval
|
|
1042
|
+
self.fallback_to_local_cache = fallback_to_local_cache
|
|
1043
|
+
self.trusted_ca_file = trusted_ca_file
|
|
1044
|
+
self.cert_file = cert_file
|
|
1045
|
+
self.key_file = key_file
|
|
1046
|
+
|
|
1047
|
+
def set_default(self):
|
|
1048
|
+
"""Use the standard local Skill endpoint when no address is assigned."""
|
|
1049
|
+
if self.addresses is None:
|
|
1050
|
+
self.addresses = ["127.0.0.1:8094"]
|
|
1051
|
+
|
|
1052
|
+
def verify(self):
|
|
1053
|
+
"""Validate the supported connector and persistence settings."""
|
|
1054
|
+
if (self.protocol != "grpc" or self.connector_type != "polaris"
|
|
1055
|
+
or not isinstance(self.addresses, (list, tuple)) or not self.addresses
|
|
1056
|
+
or any(not isinstance(address, str) or not address.strip() for address in self.addresses)):
|
|
1057
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "ai.skill requires polaris/grpc and nonempty addresses")
|
|
1058
|
+
for name in ("message_timeout", "download_timeout"):
|
|
1059
|
+
value = getattr(self, name)
|
|
1060
|
+
if isinstance(value, bool) or not isinstance(value, int) or value <= 0:
|
|
1061
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, f"ai.skill.{name} must be positive milliseconds")
|
|
1062
|
+
for name in ("persist_max_write_retry", "persist_max_read_retry", "persist_retry_interval"):
|
|
1063
|
+
value = getattr(self, name)
|
|
1064
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
1065
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, f"ai.skill.{name} must be >=0")
|
|
1066
|
+
for name in ("persist_enable", "fallback_to_local_cache"):
|
|
1067
|
+
if not isinstance(getattr(self, name), bool):
|
|
1068
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, f"ai.skill.{name} must be boolean")
|
|
1069
|
+
if not isinstance(self.token, str):
|
|
1070
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "ai.skill.token must be a string")
|
|
1071
|
+
for name in ("trusted_ca_file", "cert_file", "key_file"):
|
|
1072
|
+
if not isinstance(getattr(self, name), str):
|
|
1073
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, f"ai.skill.{name} must be a string")
|
|
1074
|
+
if bool(self.cert_file) != bool(self.key_file):
|
|
1075
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "Skill TLS certFile and keyFile must be paired")
|
|
1076
|
+
if self.persist_enable and (not isinstance(self.persist_dir, str) or not self.persist_dir):
|
|
1077
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidConfig, "ai.skill.persistDir must not be empty")
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
class SkillConfig(SelfVerifier):
|
|
1081
|
+
"""Hold the Skill connector settings."""
|
|
1082
|
+
|
|
1083
|
+
def __init__(self, server_connector=None):
|
|
1084
|
+
self.server_connector = server_connector
|
|
1085
|
+
|
|
1086
|
+
def get_server_connector(self):
|
|
1087
|
+
"""Return the independent Skill connector configuration."""
|
|
1088
|
+
return self.server_connector
|
|
1089
|
+
|
|
1090
|
+
def set_default(self):
|
|
1091
|
+
"""Fill missing connector settings."""
|
|
1092
|
+
if self.server_connector is None:
|
|
1093
|
+
self.server_connector = SkillConnectorConfig()
|
|
1094
|
+
self.server_connector.set_default()
|
|
1095
|
+
|
|
1096
|
+
def verify(self):
|
|
1097
|
+
"""Validate the Skill connector."""
|
|
1098
|
+
self.server_connector.verify()
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
class AIConfig(SelfVerifier):
|
|
1102
|
+
"""Hold AI registry client settings."""
|
|
1103
|
+
|
|
1104
|
+
def __init__(self, skill=None):
|
|
1105
|
+
self.skill = skill
|
|
1106
|
+
|
|
1107
|
+
def get_skill(self):
|
|
1108
|
+
"""Return the Skill client configuration."""
|
|
1109
|
+
return self.skill
|
|
1110
|
+
|
|
1111
|
+
def set_default(self):
|
|
1112
|
+
"""Fill missing AI configuration."""
|
|
1113
|
+
if self.skill is None:
|
|
1114
|
+
self.skill = SkillConfig()
|
|
1115
|
+
self.skill.set_default()
|
|
1116
|
+
|
|
1117
|
+
def verify(self):
|
|
1118
|
+
"""Validate configured AI capabilities."""
|
|
1119
|
+
self.skill.verify()
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
class Configuration(SelfVerifier):
|
|
1123
|
+
def __init__(self, global_config: GlobalConfig = None, consumer_config: ConsumerConfig = None,
|
|
1124
|
+
provider_config: ProviderConfig = None, ai: AIConfig = None):
|
|
1125
|
+
# Global *GlobalConfigImpl `yaml:"global"`
|
|
1126
|
+
# Consumer *ConsumerConfigImpl `yaml:"consumer"`
|
|
1127
|
+
self.global_config = global_config
|
|
1128
|
+
self.consumer_config = consumer_config
|
|
1129
|
+
self.provider_config = provider_config
|
|
1130
|
+
self.ai = ai
|
|
1131
|
+
|
|
1132
|
+
def get_ai(self):
|
|
1133
|
+
"""Return AI registry settings for this configuration."""
|
|
1134
|
+
return self.ai
|
|
1135
|
+
|
|
1136
|
+
def get_global(self) -> GlobalConfig:
|
|
1137
|
+
return self.global_config
|
|
1138
|
+
|
|
1139
|
+
def get_consumer(self) -> ConsumerConfig:
|
|
1140
|
+
return self.consumer_config
|
|
1141
|
+
|
|
1142
|
+
def get_provider(self) -> ProviderConfig:
|
|
1143
|
+
return self.provider_config
|
|
1144
|
+
|
|
1145
|
+
def verify(self):
|
|
1146
|
+
self.global_config.verify()
|
|
1147
|
+
self.consumer_config.verify()
|
|
1148
|
+
self.provider_config.verify()
|
|
1149
|
+
self.ai.verify()
|
|
1150
|
+
|
|
1151
|
+
def set_default(self):
|
|
1152
|
+
if not self.global_config:
|
|
1153
|
+
self.global_config = GlobalConfig()
|
|
1154
|
+
if not self.consumer_config:
|
|
1155
|
+
self.consumer_config = ConsumerConfig()
|
|
1156
|
+
if not self.provider_config:
|
|
1157
|
+
self.provider_config = ProviderConfig()
|
|
1158
|
+
self.global_config.set_default()
|
|
1159
|
+
self.consumer_config.set_default()
|
|
1160
|
+
self.provider_config.set_default()
|
|
1161
|
+
if self.ai is None:
|
|
1162
|
+
self.ai = AIConfig()
|
|
1163
|
+
self.ai.set_default()
|
|
1164
|
+
skill_connector = self.ai.get_skill().get_server_connector()
|
|
1165
|
+
global_connector = self.global_config.get_server_connector()
|
|
1166
|
+
if not skill_connector.token and global_connector.token:
|
|
1167
|
+
skill_connector.token = global_connector.token
|
|
1168
|
+
|
|
1169
|
+
@classmethod
|
|
1170
|
+
def from_dict(cls, data: Dict, apply_env: bool = True) -> "Configuration":
|
|
1171
|
+
"""Create a configuration from a mapping, with environment overrides by default."""
|
|
1172
|
+
from polaris.pkg.config.loader import build_configuration
|
|
1173
|
+
return build_configuration(data, apply_env=apply_env)
|
|
1174
|
+
|
|
1175
|
+
@classmethod
|
|
1176
|
+
def from_file(cls, path: str, apply_env: bool = True) -> "Configuration":
|
|
1177
|
+
"""Create a configuration from a YAML file."""
|
|
1178
|
+
from polaris.pkg.config.loader import build_configuration, load_config_file
|
|
1179
|
+
return build_configuration(load_config_file(path), apply_env=apply_env)
|
|
1180
|
+
|
|
1181
|
+
@classmethod
|
|
1182
|
+
def from_env(cls) -> "Configuration":
|
|
1183
|
+
"""Create a configuration from POLARIS_ environment variables."""
|
|
1184
|
+
from polaris.pkg.config.loader import build_configuration
|
|
1185
|
+
return build_configuration({}, apply_env=True)
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
# 服务集群信息转换为服务对象
|
|
1189
|
+
def service_cluster_to_service_key(config: ServerClusterConfig) -> ServiceKey:
|
|
1190
|
+
return ServiceKey(namespace=config.get_namespace(), service=config.get_service())
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
def get_server_services(config: Configuration) -> Dict[str, ClusterService]:
|
|
1194
|
+
"""Return configured system services, omitting unnamed clusters as the Go SDK does."""
|
|
1195
|
+
system = config.get_global().get_system()
|
|
1196
|
+
clusters = {
|
|
1197
|
+
ClusterType.DiscoverCluster: system.get_discover_cluster(),
|
|
1198
|
+
ClusterType.HealthCheckCluster: system.get_health_check_cluster(),
|
|
1199
|
+
ClusterType.MonitorCluster: system.get_monitor_cluster(),
|
|
1200
|
+
}
|
|
1201
|
+
return {
|
|
1202
|
+
cluster_type: ClusterService(service_cluster_to_service_key(cluster), cluster_type)
|
|
1203
|
+
for cluster_type, cluster in clusters.items()
|
|
1204
|
+
if cluster.get_namespace() and cluster.get_service()
|
|
1205
|
+
}
|