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,179 @@
|
|
|
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
|
+
"""Public request and result models for the read-only Skill API."""
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
|
|
20
|
+
from polaris.pkg.model.error import ErrorCode, SDKError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class SkillGetRequest:
|
|
25
|
+
"""Fetch a published skill, using the active version when version is empty."""
|
|
26
|
+
|
|
27
|
+
namespace: str
|
|
28
|
+
name: str
|
|
29
|
+
version: str = ""
|
|
30
|
+
timeout: int = 0
|
|
31
|
+
|
|
32
|
+
def validate(self):
|
|
33
|
+
"""Validate the resource identity and optional timeout in milliseconds."""
|
|
34
|
+
if (not isinstance(self.namespace, str) or not self.namespace.strip()
|
|
35
|
+
or not isinstance(self.name, str) or not self.name.strip() or not isinstance(self.version, str)
|
|
36
|
+
or type(self.timeout) is not int or self.timeout < 0):
|
|
37
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "skill namespace/name are required and timeout must be >=0")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class SkillListRequest:
|
|
42
|
+
"""Search published skills with filters, sorting and pagination."""
|
|
43
|
+
|
|
44
|
+
namespace: str = ""
|
|
45
|
+
name: str = ""
|
|
46
|
+
tag: str = ""
|
|
47
|
+
owner: str = ""
|
|
48
|
+
scope: str = ""
|
|
49
|
+
keyword: str = ""
|
|
50
|
+
order_by: str = ""
|
|
51
|
+
order_type: str = ""
|
|
52
|
+
offset: int = 0
|
|
53
|
+
limit: int = 100
|
|
54
|
+
timeout: int = 0
|
|
55
|
+
|
|
56
|
+
def validate(self):
|
|
57
|
+
"""Validate pagination and optional request timeout."""
|
|
58
|
+
if (type(self.offset) is not int or type(self.limit) is not int or type(self.timeout) is not int
|
|
59
|
+
or self.offset < 0 or self.limit < 0 or self.timeout < 0):
|
|
60
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "skill offset, limit and timeout must be nonnegative integers")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class SkillDownloadRequest(SkillGetRequest):
|
|
65
|
+
"""Download a skill as markdown or a complete ZIP archive."""
|
|
66
|
+
|
|
67
|
+
tag: str = ""
|
|
68
|
+
format: str = "markdown"
|
|
69
|
+
|
|
70
|
+
def validate(self):
|
|
71
|
+
"""Validate the identity and supported download format."""
|
|
72
|
+
super().validate()
|
|
73
|
+
if self.format not in ("markdown", "zip"):
|
|
74
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "skill format must be markdown or zip")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class SkillVersionInfo:
|
|
79
|
+
"""Version pointers published by the registry."""
|
|
80
|
+
|
|
81
|
+
active_version: str = ""
|
|
82
|
+
latest_version: str = ""
|
|
83
|
+
total_versions: int = 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class SkillStorageInfo:
|
|
88
|
+
"""Storage metadata for a skill package."""
|
|
89
|
+
|
|
90
|
+
provider: str = ""
|
|
91
|
+
files: list[str] = field(default_factory=list)
|
|
92
|
+
scope: str = ""
|
|
93
|
+
content_digest: str = ""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class SkillResource:
|
|
98
|
+
"""Registry metadata describing a published skill."""
|
|
99
|
+
|
|
100
|
+
id: int = 0
|
|
101
|
+
name: str = ""
|
|
102
|
+
namespace: str = ""
|
|
103
|
+
description: str = ""
|
|
104
|
+
status: str = ""
|
|
105
|
+
tags: list[str] = field(default_factory=list)
|
|
106
|
+
examples: list[str] = field(default_factory=list)
|
|
107
|
+
ext: dict[str, str] = field(default_factory=dict)
|
|
108
|
+
source: str = ""
|
|
109
|
+
version_info: SkillVersionInfo = field(default_factory=SkillVersionInfo)
|
|
110
|
+
meta_version: int = 0
|
|
111
|
+
scope: str = ""
|
|
112
|
+
owner: str = ""
|
|
113
|
+
download_count: int = 0
|
|
114
|
+
create_time: str = ""
|
|
115
|
+
modify_time: str = ""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass
|
|
119
|
+
class SkillResourceVersion:
|
|
120
|
+
"""Metadata for the resolved published version."""
|
|
121
|
+
|
|
122
|
+
id: int = 0
|
|
123
|
+
name: str = ""
|
|
124
|
+
namespace: str = ""
|
|
125
|
+
version: str = ""
|
|
126
|
+
author: str = ""
|
|
127
|
+
description: str = ""
|
|
128
|
+
status: str = ""
|
|
129
|
+
storage: SkillStorageInfo = field(default_factory=SkillStorageInfo)
|
|
130
|
+
download_count: int = 0
|
|
131
|
+
create_time: str = ""
|
|
132
|
+
modify_time: str = ""
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass
|
|
136
|
+
class SkillGetResponse:
|
|
137
|
+
"""A published skill and its SKILL.md content."""
|
|
138
|
+
|
|
139
|
+
resource: SkillResource
|
|
140
|
+
version: SkillResourceVersion
|
|
141
|
+
content: str
|
|
142
|
+
loaded_from_file: bool = False
|
|
143
|
+
code: int = 200000
|
|
144
|
+
info: str = ""
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def resource_version(self) -> SkillResourceVersion:
|
|
148
|
+
"""Java-compatible name for the resolved version metadata."""
|
|
149
|
+
return self.version
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass
|
|
153
|
+
class SkillListResponse:
|
|
154
|
+
"""One page of skill search results."""
|
|
155
|
+
|
|
156
|
+
total: int
|
|
157
|
+
resources: list[SkillResource]
|
|
158
|
+
code: int = 200000
|
|
159
|
+
info: str = ""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class SkillDownloadResponse:
|
|
164
|
+
"""A complete markdown document or ZIP package."""
|
|
165
|
+
|
|
166
|
+
version: str
|
|
167
|
+
content_type: str
|
|
168
|
+
content: str = ""
|
|
169
|
+
data: bytes = b""
|
|
170
|
+
total_size: int = 0
|
|
171
|
+
filename: str = ""
|
|
172
|
+
loaded_from_file: bool = False
|
|
173
|
+
code: int = 200000
|
|
174
|
+
info: str = ""
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def zip_content(self) -> bytes:
|
|
178
|
+
"""Java-compatible name for the assembled ZIP payload."""
|
|
179
|
+
return self.data
|
|
@@ -0,0 +1,168 @@
|
|
|
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 time
|
|
17
|
+
import threading
|
|
18
|
+
from typing import List, Tuple
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InstanceGauge:
|
|
22
|
+
def get_id(self) -> str:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def get_namespace(self) -> str:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def get_service(self) -> str:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def get_api(self) -> str:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def get_host(self) -> str:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def get_port(self) -> int:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def get_ret_status(self) -> int:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def get_ret_code(self) -> int:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def get_delay(self) -> int:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def validate(self) -> bool:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BucketMetric:
|
|
54
|
+
def __init__(self):
|
|
55
|
+
self.success = 0
|
|
56
|
+
self.total_failed = 0
|
|
57
|
+
self.continuous_failed = 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Bucket:
|
|
61
|
+
def __init__(self, begin_time=0, end_time=0):
|
|
62
|
+
self.lock = threading.RLock()
|
|
63
|
+
self.begin_time = begin_time
|
|
64
|
+
self.end_time = end_time
|
|
65
|
+
self.metric = BucketMetric()
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def success(self):
|
|
69
|
+
return self.metric.success
|
|
70
|
+
|
|
71
|
+
@success.setter
|
|
72
|
+
def success(self, val):
|
|
73
|
+
self.metric.success = val
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def total_failed(self):
|
|
77
|
+
return self.metric.total_failed
|
|
78
|
+
|
|
79
|
+
@total_failed.setter
|
|
80
|
+
def total_failed(self, val):
|
|
81
|
+
self.metric.total_failed = val
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def continuous_failed(self):
|
|
85
|
+
return self.metric.continuous_failed
|
|
86
|
+
|
|
87
|
+
@continuous_failed.setter
|
|
88
|
+
def continuous_failed(self, val):
|
|
89
|
+
self.metric.continuous_failed = val
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SlidingWindow:
|
|
93
|
+
def __init__(self, bucket_count: int, bucket_interval: int):
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
:param bucket_count: 窗口内桶总数
|
|
97
|
+
:param bucket_interval: 桶的时长(ms)
|
|
98
|
+
"""
|
|
99
|
+
self.win_chain: List[Bucket] = []
|
|
100
|
+
self.bucket_count = bucket_count
|
|
101
|
+
self.bucket_interval = bucket_interval
|
|
102
|
+
self.lock = threading.RLock()
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def window_length(self):
|
|
106
|
+
return self.bucket_count * self.bucket_interval
|
|
107
|
+
|
|
108
|
+
def _bucket_for(self, now: int) -> Bucket:
|
|
109
|
+
tail = self.win_chain[-1] if self.win_chain else None
|
|
110
|
+
if tail is not None and tail.begin_time <= now < tail.end_time:
|
|
111
|
+
return tail
|
|
112
|
+
|
|
113
|
+
# A long idle gap recreates the window; otherwise append the bucket
|
|
114
|
+
# that contains the current timestamp.
|
|
115
|
+
if tail is None or tail.end_time + self.window_length <= now:
|
|
116
|
+
self.win_chain = [Bucket(now, now + self.bucket_interval)]
|
|
117
|
+
return self.win_chain[0]
|
|
118
|
+
|
|
119
|
+
next_start = tail.end_time + (now - tail.end_time) // self.bucket_interval
|
|
120
|
+
bucket = Bucket(next_start, next_start + self.bucket_interval)
|
|
121
|
+
while (self.win_chain and
|
|
122
|
+
self.win_chain[0].begin_time + self.window_length <= next_start):
|
|
123
|
+
self.win_chain.pop(0)
|
|
124
|
+
self.win_chain.append(bucket)
|
|
125
|
+
return bucket
|
|
126
|
+
|
|
127
|
+
def add(self, value, cb, *, now=None):
|
|
128
|
+
if now is None:
|
|
129
|
+
now = int(time.time() * 1000)
|
|
130
|
+
with self.lock:
|
|
131
|
+
cur_bucket = self._bucket_for(now)
|
|
132
|
+
with cur_bucket.lock:
|
|
133
|
+
ret = cb(cur_bucket, value)
|
|
134
|
+
return ret
|
|
135
|
+
|
|
136
|
+
def lookup(self, cur_time: int) -> Bucket:
|
|
137
|
+
with self.lock:
|
|
138
|
+
return self._bucket_for(cur_time)
|
|
139
|
+
|
|
140
|
+
def calc_interval_values(self, begin_time: int, end_time: int) -> Tuple:
|
|
141
|
+
total_success = 0
|
|
142
|
+
total_failed = 0
|
|
143
|
+
continuous_failed = 0
|
|
144
|
+
|
|
145
|
+
with self.lock:
|
|
146
|
+
for b in self.win_chain:
|
|
147
|
+
with b.lock:
|
|
148
|
+
if begin_time >= b.end_time:
|
|
149
|
+
continue
|
|
150
|
+
if end_time <= b.begin_time:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
total_success += b.success
|
|
154
|
+
total_failed += b.total_failed
|
|
155
|
+
continuous_failed += b.continuous_failed
|
|
156
|
+
|
|
157
|
+
return total_success, total_failed, continuous_failed
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
ApiGetOneInstance = "Consumer::GetOneInstance"
|
|
161
|
+
ApiGetInstances = "Consumer::GetInstances"
|
|
162
|
+
ApiAsyncGetOneInstance = "Consumer::AsyncGetOneInstance"
|
|
163
|
+
ApiAsyncGetInstances = "Consumer::AsyncGetInstances"
|
|
164
|
+
ApiGetRouteRule = "Consumer::GetRouteRule"
|
|
165
|
+
|
|
166
|
+
ApiRegister = "Provider::Register"
|
|
167
|
+
ApiDeregister = "Provider::Deregister"
|
|
168
|
+
ApiHeartbeat = "Provider::Heartbeat"
|
|
@@ -0,0 +1,58 @@
|
|
|
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 asyncio
|
|
17
|
+
import ipaddress
|
|
18
|
+
import socket
|
|
19
|
+
from contextlib import suppress
|
|
20
|
+
|
|
21
|
+
import psutil
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_ip(ifname: str) -> str:
|
|
25
|
+
"""Return an IP from the named interface without falling back to another one."""
|
|
26
|
+
for address in psutil.net_if_addrs().get(ifname, ()):
|
|
27
|
+
if address.family in (socket.AF_INET, socket.AF_INET6):
|
|
28
|
+
return str(ipaddress.ip_address(address.address.split("%", 1)[0]))
|
|
29
|
+
raise OSError(f"no IP address found for interface {ifname}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def get_local_ip(address: str, timeout: int) -> str:
|
|
33
|
+
"""Probe the TCP source IP for a configured server within a millisecond timeout."""
|
|
34
|
+
host, port = get_ip_from_int_with_port(address)
|
|
35
|
+
if not host or not 0 < port < 65536:
|
|
36
|
+
raise ValueError(f"invalid server address {address}")
|
|
37
|
+
async with asyncio.timeout(timeout / 1000):
|
|
38
|
+
_, writer = await asyncio.open_connection(host, port)
|
|
39
|
+
try:
|
|
40
|
+
local_address = writer.get_extra_info("sockname")
|
|
41
|
+
if not local_address or not local_address[0]:
|
|
42
|
+
raise OSError("TCP connection has no local IP address")
|
|
43
|
+
return str(ipaddress.ip_address(local_address[0]))
|
|
44
|
+
finally:
|
|
45
|
+
writer.close()
|
|
46
|
+
with suppress(OSError):
|
|
47
|
+
await writer.wait_closed()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_ip_from_int_with_port(ipstr):
|
|
51
|
+
host, port = ipstr.rsplit(":", 1)
|
|
52
|
+
if host.startswith("[") and host.endswith("]"):
|
|
53
|
+
host = host[1:-1]
|
|
54
|
+
try:
|
|
55
|
+
host = str(ipaddress.IPv4Address(int(host)))
|
|
56
|
+
except ValueError:
|
|
57
|
+
pass
|
|
58
|
+
return host, int(port)
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
from polaris._version import __version__
|
|
17
|
+
|
|
18
|
+
version = __version__
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
from . import circuit_breaker
|
|
17
|
+
from . import event
|
|
18
|
+
from . import load_balancer
|
|
19
|
+
from . import local_registry
|
|
20
|
+
from . import server_connector
|
|
21
|
+
from . import service_router
|
|
22
|
+
from . import stat_reporter
|
|
23
|
+
from .skill import connector
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"circuit_breaker",
|
|
27
|
+
"event",
|
|
28
|
+
"load_balancer",
|
|
29
|
+
"local_registry",
|
|
30
|
+
"server_connector",
|
|
31
|
+
"service_router",
|
|
32
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
from .base import BaseCircuitBreaker, CircuitBreakResult
|
|
17
|
+
from .composite import CircuitBreaker
|
|
18
|
+
|
|
19
|
+
__all__ = ["BaseCircuitBreaker", "CircuitBreakResult", "CircuitBreaker"]
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
from typing import Dict, List
|
|
18
|
+
|
|
19
|
+
from polaris.pkg.model.service import Instance
|
|
20
|
+
|
|
21
|
+
__all__ = ["BaseCircuitBreaker", "CircuitBreakResult"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CircuitBreakResult:
|
|
25
|
+
def __init__(self, ts=None):
|
|
26
|
+
self.now = ts
|
|
27
|
+
self.instance_2_open = []
|
|
28
|
+
self.instance_2_halfopen = []
|
|
29
|
+
self.instance_2_close = []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BaseCircuitBreaker(ABC):
|
|
33
|
+
"""
|
|
34
|
+
熔断判断返回是否需要进行熔断
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def realtime_circuit_break(self, instance: Instance, ret: int, cost: int) -> bool:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def timing_circuit_break(self, instance_list: List[Instance]) -> CircuitBreakResult:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def next_check_time(self, instance: Instance):
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def now(self):
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
def on_state_change(self, instance: Instance):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def get_half_open_request_count(self):
|
|
54
|
+
return 3
|