sycommon-python-lib 0.1.46__py3-none-any.whl → 0.1.57b1__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.
- sycommon/config/Config.py +29 -4
- sycommon/config/LangfuseConfig.py +15 -0
- sycommon/config/RerankerConfig.py +1 -0
- sycommon/config/SentryConfig.py +13 -0
- sycommon/database/async_base_db_service.py +36 -0
- sycommon/database/async_database_service.py +96 -0
- sycommon/llm/__init__.py +0 -0
- sycommon/llm/embedding.py +204 -0
- sycommon/llm/get_llm.py +37 -0
- sycommon/llm/llm_logger.py +126 -0
- sycommon/llm/llm_tokens.py +119 -0
- sycommon/llm/struct_token.py +192 -0
- sycommon/llm/sy_langfuse.py +103 -0
- sycommon/llm/usage_token.py +117 -0
- sycommon/logging/async_sql_logger.py +65 -0
- sycommon/logging/kafka_log.py +200 -434
- sycommon/logging/logger_levels.py +23 -0
- sycommon/middleware/context.py +2 -0
- sycommon/middleware/exception.py +10 -16
- sycommon/middleware/timeout.py +2 -1
- sycommon/middleware/traceid.py +179 -51
- sycommon/notice/__init__.py +0 -0
- sycommon/notice/uvicorn_monitor.py +200 -0
- sycommon/rabbitmq/rabbitmq_client.py +267 -290
- sycommon/rabbitmq/rabbitmq_pool.py +277 -465
- sycommon/rabbitmq/rabbitmq_service.py +23 -891
- sycommon/rabbitmq/rabbitmq_service_client_manager.py +211 -0
- sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +73 -0
- sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +285 -0
- sycommon/rabbitmq/rabbitmq_service_core.py +117 -0
- sycommon/rabbitmq/rabbitmq_service_producer_manager.py +238 -0
- sycommon/sentry/__init__.py +0 -0
- sycommon/sentry/sy_sentry.py +35 -0
- sycommon/services.py +144 -115
- sycommon/synacos/feign.py +18 -7
- sycommon/synacos/feign_client.py +26 -8
- sycommon/synacos/nacos_client_base.py +119 -0
- sycommon/synacos/nacos_config_manager.py +107 -0
- sycommon/synacos/nacos_heartbeat_manager.py +144 -0
- sycommon/synacos/nacos_service.py +65 -769
- sycommon/synacos/nacos_service_discovery.py +157 -0
- sycommon/synacos/nacos_service_registration.py +270 -0
- sycommon/tools/env.py +62 -0
- sycommon/tools/merge_headers.py +117 -0
- sycommon/tools/snowflake.py +238 -23
- {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/METADATA +18 -11
- sycommon_python_lib-0.1.57b1.dist-info/RECORD +89 -0
- sycommon_python_lib-0.1.46.dist-info/RECORD +0 -59
- {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.46.dist-info → sycommon_python_lib-0.1.57b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from typing import List, Dict
|
|
3
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
4
|
+
from sycommon.synacos.nacos_client_base import NacosClientBase
|
|
5
|
+
from sycommon.synacos.nacos_service_registration import NacosServiceRegistration
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NacosServiceDiscovery:
|
|
9
|
+
"""Nacos服务发现类 - 负责服务实例发现和轮询"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, client_base: NacosClientBase):
|
|
12
|
+
self.client_base = client_base
|
|
13
|
+
|
|
14
|
+
# 轮询管理
|
|
15
|
+
self._round_robin_index = 0
|
|
16
|
+
self._round_robin_lock = threading.Lock()
|
|
17
|
+
|
|
18
|
+
# 连接监控配置
|
|
19
|
+
self.connection_check_interval = self.client_base.nacos_config.get(
|
|
20
|
+
'connectionCheckInterval', 30)
|
|
21
|
+
self._monitor_thread_started = False
|
|
22
|
+
self._monitor_thread_lock = threading.Lock()
|
|
23
|
+
|
|
24
|
+
def discover_services(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
|
|
25
|
+
"""发现服务实例列表 (与Java格式兼容)"""
|
|
26
|
+
if not self.client_base.ensure_client_connected():
|
|
27
|
+
return []
|
|
28
|
+
|
|
29
|
+
return self.get_service_instances(service_name, group, version)
|
|
30
|
+
|
|
31
|
+
def get_service_instances(self, service_name: str, group: str = "DEFAULT_GROUP", target_version: str = None) -> List[Dict]:
|
|
32
|
+
"""
|
|
33
|
+
获取服务实例列表,并按照以下优先级规则筛选:
|
|
34
|
+
1. 相同版本号的实例
|
|
35
|
+
2. 无版本号的实例
|
|
36
|
+
3. 所有实例中轮询
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
namespace_id = self.client_base.nacos_config['namespaceId']
|
|
40
|
+
instances = self.client_base.nacos_client.list_naming_instance(
|
|
41
|
+
service_name,
|
|
42
|
+
namespace_id=namespace_id,
|
|
43
|
+
group_name=group,
|
|
44
|
+
healthy_only=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if not instances or 'hosts' not in instances:
|
|
48
|
+
SYLogger.info(f"nacos:未发现 {service_name} 的服务实例")
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
all_instances = instances.get('hosts', [])
|
|
52
|
+
all_instances = [
|
|
53
|
+
instance for instance in all_instances
|
|
54
|
+
if instance.get('enabled', True)
|
|
55
|
+
]
|
|
56
|
+
SYLogger.info(
|
|
57
|
+
f"nacos:共发现 {len(all_instances)} 个 {service_name} 服务实例")
|
|
58
|
+
|
|
59
|
+
version_to_use = target_version
|
|
60
|
+
|
|
61
|
+
if version_to_use:
|
|
62
|
+
same_version_instances = [
|
|
63
|
+
instance for instance in all_instances
|
|
64
|
+
if instance.get('metadata', {}).get('version') == version_to_use
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
if same_version_instances:
|
|
68
|
+
SYLogger.info(
|
|
69
|
+
f"nacos:筛选出 {len(same_version_instances)} 个与当前版本({version_to_use})匹配的实例")
|
|
70
|
+
return same_version_instances
|
|
71
|
+
|
|
72
|
+
no_version_instances = [
|
|
73
|
+
instance for instance in all_instances
|
|
74
|
+
if 'version' not in instance.get('metadata', {})
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
if no_version_instances:
|
|
78
|
+
SYLogger.info(
|
|
79
|
+
f"nacos:未找到相同版本({version_to_use})的实例,筛选出 {len(no_version_instances)} 个无版本号的实例")
|
|
80
|
+
return no_version_instances
|
|
81
|
+
else:
|
|
82
|
+
no_version_instances = [
|
|
83
|
+
instance for instance in all_instances
|
|
84
|
+
if 'version' not in instance.get('metadata', {})
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
if no_version_instances:
|
|
88
|
+
# 从通用实例中轮询
|
|
89
|
+
with self._round_robin_lock:
|
|
90
|
+
selected_index = self._round_robin_index % len(
|
|
91
|
+
no_version_instances)
|
|
92
|
+
self._round_robin_index = (
|
|
93
|
+
selected_index + 1) % len(no_version_instances)
|
|
94
|
+
|
|
95
|
+
SYLogger.info(
|
|
96
|
+
f"nacos:无版本请求,从 {len(no_version_instances)} 个通用实例中选择")
|
|
97
|
+
return [no_version_instances[selected_index]]
|
|
98
|
+
|
|
99
|
+
SYLogger.info(
|
|
100
|
+
f"nacos:使用轮询方式从 {len(all_instances)} 个实例中选择")
|
|
101
|
+
|
|
102
|
+
with self._round_robin_lock:
|
|
103
|
+
selected_index = self._round_robin_index % len(all_instances)
|
|
104
|
+
self._round_robin_index = (
|
|
105
|
+
selected_index + 1) % len(all_instances)
|
|
106
|
+
|
|
107
|
+
return [all_instances[selected_index]]
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
SYLogger.error(f"nacos:服务发现失败: {service_name}: {str(e)}")
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
def monitor_connection(self, registration: NacosServiceRegistration):
|
|
114
|
+
"""连接监控线程"""
|
|
115
|
+
with self._monitor_thread_lock:
|
|
116
|
+
if self.client_base._shutdown_event.is_set() or self._monitor_thread_started:
|
|
117
|
+
SYLogger.warning("nacos:监控线程已启动/已关闭,拒绝重复启动")
|
|
118
|
+
return
|
|
119
|
+
self._monitor_thread_started = True
|
|
120
|
+
|
|
121
|
+
check_interval = self.connection_check_interval
|
|
122
|
+
SYLogger.info(
|
|
123
|
+
f"nacos:连接监控线程启动 - 线程ID: {threading.current_thread().ident}")
|
|
124
|
+
|
|
125
|
+
while not self.client_base._shutdown_event.is_set():
|
|
126
|
+
try:
|
|
127
|
+
if not self.client_base.is_connected:
|
|
128
|
+
SYLogger.warning("nacos:客户端未连接,尝试重新初始化")
|
|
129
|
+
self.client_base.ensure_client_connected()
|
|
130
|
+
else:
|
|
131
|
+
current_registered = registration.check_service_registered()
|
|
132
|
+
|
|
133
|
+
if current_registered != registration.registered:
|
|
134
|
+
registration.registered = current_registered
|
|
135
|
+
if not current_registered:
|
|
136
|
+
SYLogger.warning("nacos:服务实例未注册,触发单次重新注册")
|
|
137
|
+
retry_thread = threading.Thread(
|
|
138
|
+
target=registration.register,
|
|
139
|
+
args=(True,),
|
|
140
|
+
daemon=True,
|
|
141
|
+
name="NacosSingleRetryThread"
|
|
142
|
+
)
|
|
143
|
+
retry_thread.start()
|
|
144
|
+
else:
|
|
145
|
+
SYLogger.info("nacos:服务实例已注册,触发单次验证")
|
|
146
|
+
registration.verify_registration()
|
|
147
|
+
|
|
148
|
+
self.client_base._shutdown_event.wait(check_interval)
|
|
149
|
+
|
|
150
|
+
except Exception as e:
|
|
151
|
+
SYLogger.error(f"nacos:连接监控异常: {str(e)}")
|
|
152
|
+
self.client_base._shutdown_event.wait(5)
|
|
153
|
+
|
|
154
|
+
with self._monitor_thread_lock:
|
|
155
|
+
self._monitor_thread_started = False
|
|
156
|
+
SYLogger.info(
|
|
157
|
+
f"nacos:连接监控线程退出 - 线程ID: {threading.current_thread().ident}")
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import atexit
|
|
4
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
5
|
+
from sycommon.synacos.nacos_client_base import NacosClientBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NacosServiceRegistration:
|
|
9
|
+
"""Nacos服务注册类 - 负责服务注册、注销和状态验证"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, client_base: NacosClientBase, service_name: str, real_ip: str, port: int, version: str):
|
|
12
|
+
self.client_base = client_base
|
|
13
|
+
self.service_name = service_name
|
|
14
|
+
self.real_ip = real_ip
|
|
15
|
+
self.port = port
|
|
16
|
+
self.version = version
|
|
17
|
+
|
|
18
|
+
# 注册配置
|
|
19
|
+
self.register_retry_interval = self.client_base.nacos_config.get(
|
|
20
|
+
'registerRetryInterval', 15)
|
|
21
|
+
self.long_term_retry_delay = self.client_base.nacos_config.get(
|
|
22
|
+
'longTermRetryDelay', 30)
|
|
23
|
+
self.max_long_term_retries = self.client_base.nacos_config.get(
|
|
24
|
+
'maxLongTermRetries', -1)
|
|
25
|
+
|
|
26
|
+
# 验证配置
|
|
27
|
+
self.registration_verify_count = self.client_base.nacos_config.get(
|
|
28
|
+
'registrationVerifyCount', 1)
|
|
29
|
+
self.registration_verify_interval = self.client_base.nacos_config.get(
|
|
30
|
+
'registrationVerifyInterval', 1)
|
|
31
|
+
self.registration_post_delay = self.client_base.nacos_config.get(
|
|
32
|
+
'registrationPostDelay', 3)
|
|
33
|
+
|
|
34
|
+
# 状态管理
|
|
35
|
+
self.registered = False
|
|
36
|
+
self._long_term_retry_count = 0
|
|
37
|
+
self._verify_lock = threading.Lock()
|
|
38
|
+
self._last_verify_time = 0
|
|
39
|
+
self._atexit_registered = False
|
|
40
|
+
|
|
41
|
+
def _cleanup_stale_instance(self):
|
|
42
|
+
"""清理可能存在的残留实例"""
|
|
43
|
+
if not self.client_base.is_connected:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
self.client_base.nacos_client.remove_naming_instance(
|
|
48
|
+
service_name=self.service_name,
|
|
49
|
+
ip=self.real_ip,
|
|
50
|
+
port=int(self.port),
|
|
51
|
+
cluster_name="DEFAULT"
|
|
52
|
+
)
|
|
53
|
+
SYLogger.warning(f"nacos:清理残留实例: {self.real_ip}:{self.port}")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
SYLogger.error(f"nacos:清理残留实例异常: {e}")
|
|
56
|
+
|
|
57
|
+
def check_service_registered(self) -> bool:
|
|
58
|
+
"""检查服务是否已注册(基于实例列表)"""
|
|
59
|
+
if not self.client_base.enable_register_nacos:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
if not self.client_base.ensure_client_connected():
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
namespace_id = self.client_base.nacos_config['namespaceId']
|
|
67
|
+
instances = self.client_base.nacos_client.list_naming_instance(
|
|
68
|
+
service_name=self.service_name,
|
|
69
|
+
namespace_id=namespace_id,
|
|
70
|
+
group_name="DEFAULT_GROUP",
|
|
71
|
+
healthy_only=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
found = False
|
|
75
|
+
for instance in instances.get('hosts', []):
|
|
76
|
+
if (instance.get('ip') == self.real_ip and
|
|
77
|
+
instance.get('port') == int(self.port)):
|
|
78
|
+
SYLogger.info(f"nacos:找到已注册实例: {self.real_ip}:{self.port}")
|
|
79
|
+
found = True
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
if not found:
|
|
83
|
+
SYLogger.warning(f"nacos:未找到注册实例: {self.real_ip}:{self.port}")
|
|
84
|
+
|
|
85
|
+
self.registered = found
|
|
86
|
+
return found
|
|
87
|
+
except Exception as e:
|
|
88
|
+
SYLogger.error(f"nacos:检查服务注册状态失败: {e}")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
def verify_registration(self) -> bool:
|
|
92
|
+
"""多次验证服务是否成功注册(加锁防止重复执行)"""
|
|
93
|
+
if self._verify_lock.locked():
|
|
94
|
+
SYLogger.warning("nacos:注册验证已在执行中,跳过重复调用")
|
|
95
|
+
return self.registered
|
|
96
|
+
|
|
97
|
+
with self._verify_lock:
|
|
98
|
+
current_time = time.time()
|
|
99
|
+
if current_time - self._last_verify_time < 15: # 15秒冷却
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
success_count = 0
|
|
103
|
+
verify_count = self.registration_verify_count
|
|
104
|
+
SYLogger.info(
|
|
105
|
+
f"nacos:开始验证服务注册状态,共验证 {verify_count} 次,间隔 {self.registration_verify_interval} 秒")
|
|
106
|
+
|
|
107
|
+
self._last_verify_time = current_time
|
|
108
|
+
|
|
109
|
+
for i in range(verify_count):
|
|
110
|
+
if self.check_service_registered():
|
|
111
|
+
success_count += 1
|
|
112
|
+
|
|
113
|
+
if i < verify_count - 1:
|
|
114
|
+
self.client_base._shutdown_event.wait(
|
|
115
|
+
self.registration_verify_interval)
|
|
116
|
+
if self.client_base._shutdown_event.is_set():
|
|
117
|
+
SYLogger.warning("nacos:应用正在关闭,终止注册验证")
|
|
118
|
+
break
|
|
119
|
+
|
|
120
|
+
pass_threshold = verify_count / 2
|
|
121
|
+
result = success_count >= pass_threshold
|
|
122
|
+
|
|
123
|
+
if result:
|
|
124
|
+
SYLogger.info(
|
|
125
|
+
f"nacos:服务注册验证成功,{success_count}/{verify_count} 次验证通过")
|
|
126
|
+
else:
|
|
127
|
+
SYLogger.error(
|
|
128
|
+
f"nacos:服务注册验证失败,仅 {success_count}/{verify_count} 次验证通过")
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
def register(self, force: bool = False) -> bool:
|
|
133
|
+
"""注册服务到Nacos"""
|
|
134
|
+
if self.registered and not force and self.check_service_registered():
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
if self.registered and not force:
|
|
138
|
+
self.registered = False
|
|
139
|
+
SYLogger.warning("nacos:本地状态显示已注册,但Nacos中未找到服务实例,准备重新注册")
|
|
140
|
+
|
|
141
|
+
metadata = {
|
|
142
|
+
"ignore-metrics": "true",
|
|
143
|
+
}
|
|
144
|
+
if self.version:
|
|
145
|
+
metadata["version"] = self.version
|
|
146
|
+
|
|
147
|
+
for attempt in range(self.client_base.max_retries):
|
|
148
|
+
if not self.client_base.ensure_client_connected():
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
self.client_base.nacos_client.add_naming_instance(
|
|
153
|
+
service_name=self.service_name,
|
|
154
|
+
ip=self.real_ip,
|
|
155
|
+
port=int(self.port),
|
|
156
|
+
metadata=metadata,
|
|
157
|
+
cluster_name="DEFAULT",
|
|
158
|
+
healthy=True,
|
|
159
|
+
ephemeral=True,
|
|
160
|
+
heartbeat_interval=15 # 心跳间隔默认15秒
|
|
161
|
+
)
|
|
162
|
+
SYLogger.info(
|
|
163
|
+
f"nacos:服务 {self.service_name} 注册请求已发送: {self.real_ip}:{self.port}")
|
|
164
|
+
|
|
165
|
+
if not self._atexit_registered:
|
|
166
|
+
atexit.register(self.deregister_service)
|
|
167
|
+
self._atexit_registered = True
|
|
168
|
+
|
|
169
|
+
return True
|
|
170
|
+
except Exception as e:
|
|
171
|
+
if "signal only works in main thread" in str(e):
|
|
172
|
+
return True
|
|
173
|
+
elif attempt < self.client_base.max_retries - 1:
|
|
174
|
+
SYLogger.warning(
|
|
175
|
+
f"nacos:服务注册失败 (尝试 {attempt+1}/{self.client_base.max_retries}): {e}")
|
|
176
|
+
time.sleep(self.client_base.retry_delay)
|
|
177
|
+
else:
|
|
178
|
+
SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {e}")
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def register_with_retry(self) -> bool:
|
|
182
|
+
"""带重试机制的服务注册(基于实例列表检查)"""
|
|
183
|
+
retry_count = 0
|
|
184
|
+
last_error = None
|
|
185
|
+
self.registered = False
|
|
186
|
+
# 首次注册尝试标记
|
|
187
|
+
first_attempt = True
|
|
188
|
+
|
|
189
|
+
while (not self.registered) and (self.max_long_term_retries < 0 or retry_count < self.max_long_term_retries):
|
|
190
|
+
if self.registered:
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
register_success = self.register(force=True)
|
|
195
|
+
if not register_success:
|
|
196
|
+
raise RuntimeError("nacos:服务注册请求失败")
|
|
197
|
+
|
|
198
|
+
SYLogger.info(
|
|
199
|
+
f"nacos:服务注册请求已发送,{'首次启动信任注册,跳过阻塞验证' if first_attempt else f'延迟 {self.registration_post_delay} 秒后开始验证'}")
|
|
200
|
+
|
|
201
|
+
# 核心逻辑:首次注册跳过阻塞验证,非首次按原逻辑
|
|
202
|
+
if first_attempt:
|
|
203
|
+
# 首次注册:直接标记成功,不阻塞
|
|
204
|
+
self.registered = True
|
|
205
|
+
self.client_base._client_initialized = True
|
|
206
|
+
self.client_base._shutdown_event.set()
|
|
207
|
+
self.client_base._shutdown_event.clear()
|
|
208
|
+
self._long_term_retry_count = 0
|
|
209
|
+
|
|
210
|
+
SYLogger.info(f"nacos:首次启动信任注册成功: {self.service_name}")
|
|
211
|
+
first_attempt = False # 标记为非首次
|
|
212
|
+
return True
|
|
213
|
+
else:
|
|
214
|
+
# 非首次/重试:保留原有阻塞验证逻辑
|
|
215
|
+
time.sleep(self.registration_post_delay)
|
|
216
|
+
registered = self.verify_registration()
|
|
217
|
+
self.registered = registered
|
|
218
|
+
|
|
219
|
+
if self.registered:
|
|
220
|
+
self.client_base._client_initialized = True
|
|
221
|
+
self.client_base._shutdown_event.set()
|
|
222
|
+
self.client_base._shutdown_event.clear()
|
|
223
|
+
self._long_term_retry_count = 0
|
|
224
|
+
|
|
225
|
+
SYLogger.info(
|
|
226
|
+
f"nacos:服务注册成功并通过验证: {self.service_name}")
|
|
227
|
+
return True
|
|
228
|
+
else:
|
|
229
|
+
raise RuntimeError("nacos:服务注册验证失败")
|
|
230
|
+
|
|
231
|
+
except Exception as e:
|
|
232
|
+
last_error = str(e)
|
|
233
|
+
retry_count += 1
|
|
234
|
+
first_attempt = False # 失败后标记为非首次
|
|
235
|
+
delay = min(self.register_retry_interval,
|
|
236
|
+
self.client_base.max_retry_delay)
|
|
237
|
+
|
|
238
|
+
SYLogger.warning(
|
|
239
|
+
f"nacos:服务注册尝试 {retry_count} 失败: {last_error},{delay}秒后重试")
|
|
240
|
+
time.sleep(delay)
|
|
241
|
+
|
|
242
|
+
if self.registered:
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
if last_error:
|
|
246
|
+
SYLogger.error(f"nacos:服务注册失败,最终错误: {last_error}")
|
|
247
|
+
else:
|
|
248
|
+
SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {self.service_name}")
|
|
249
|
+
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
def deregister_service(self):
|
|
253
|
+
"""从Nacos注销服务"""
|
|
254
|
+
if not self.registered or not self.client_base.is_connected:
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
SYLogger.info("nacos:正在注销服务...")
|
|
258
|
+
try:
|
|
259
|
+
self.client_base.nacos_client.remove_naming_instance(
|
|
260
|
+
service_name=self.service_name,
|
|
261
|
+
ip=self.real_ip,
|
|
262
|
+
port=int(self.port),
|
|
263
|
+
cluster_name="DEFAULT"
|
|
264
|
+
)
|
|
265
|
+
self.registered = False
|
|
266
|
+
SYLogger.info(f"nacos:服务 {self.service_name} 已注销")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
SYLogger.error(f"nacos:注销服务时发生错误: {e}")
|
|
269
|
+
finally:
|
|
270
|
+
self.client_base._shutdown_event.set()
|
sycommon/tools/env.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _normalize_env_key(key: str) -> str:
|
|
5
|
+
"""
|
|
6
|
+
环境变量名标准化:
|
|
7
|
+
1. 转小写
|
|
8
|
+
2. 中划线(-)和下划线(_)统一替换为下划线(_)
|
|
9
|
+
:param key: 原始环境变量名
|
|
10
|
+
:return: 标准化后的key
|
|
11
|
+
"""
|
|
12
|
+
return key.lower().replace('-', '_')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def check_env_flag(target_keys: list, default: str = 'false') -> bool:
|
|
16
|
+
"""
|
|
17
|
+
检查环境变量是否为"true"(自动兼容:大小写、中划线/下划线)
|
|
18
|
+
:param target_keys: 目标变量名列表(如 ['REGISTER-NACOS'],无需传双key)
|
|
19
|
+
:param default: 默认值(未找到变量时使用)
|
|
20
|
+
:return: 布尔值
|
|
21
|
+
"""
|
|
22
|
+
# 1. 标准化目标key(小写+统一下划线)
|
|
23
|
+
target_keys_normalized = [_normalize_env_key(k) for k in target_keys]
|
|
24
|
+
|
|
25
|
+
# 2. 遍历所有环境变量,标准化后匹配
|
|
26
|
+
for env_key, env_val in os.environ.items():
|
|
27
|
+
env_key_normalized = _normalize_env_key(env_key)
|
|
28
|
+
if env_key_normalized in target_keys_normalized:
|
|
29
|
+
# 3. 值去空格 + 转小写 判断
|
|
30
|
+
return env_val.strip().lower() == 'true'
|
|
31
|
+
|
|
32
|
+
# 4. 未找到变量时,判断默认值
|
|
33
|
+
return default.strip().lower() == 'true'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_env_var(key: str, default='', case_insensitive: bool = True) -> str:
|
|
37
|
+
"""
|
|
38
|
+
获取环境变量值(自动兼容:大小写、中划线/下划线)
|
|
39
|
+
:param key: 目标环境变量名(如 'REGISTER-NACOS'/'version')
|
|
40
|
+
:param default: 无匹配时的默认值,默认空字符串
|
|
41
|
+
:param case_insensitive: 是否忽略变量名大小写(默认True,建议保持)
|
|
42
|
+
:return: 匹配到的环境变量值 / 默认值
|
|
43
|
+
"""
|
|
44
|
+
if case_insensitive:
|
|
45
|
+
# 标准化目标key(小写+统一下划线)
|
|
46
|
+
target_key_normalized = _normalize_env_key(key)
|
|
47
|
+
|
|
48
|
+
# 遍历环境变量,标准化后匹配
|
|
49
|
+
for env_key, env_val in os.environ.items():
|
|
50
|
+
env_key_normalized = _normalize_env_key(env_key)
|
|
51
|
+
if env_key_normalized == target_key_normalized:
|
|
52
|
+
return env_val
|
|
53
|
+
return default
|
|
54
|
+
else:
|
|
55
|
+
# 不忽略大小写时,仅自动兼容中划线/下划线
|
|
56
|
+
target_key_1 = key # 原始key
|
|
57
|
+
target_key_2 = key.replace(
|
|
58
|
+
'-', '_') if '-' in key else key.replace('_', '-') # 替换格式的key
|
|
59
|
+
val = os.getenv(target_key_1)
|
|
60
|
+
if val is not None:
|
|
61
|
+
return val
|
|
62
|
+
return os.getenv(target_key_2, default)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
def merge_headers(
|
|
2
|
+
source_headers, # 来源headers(支持多种格式:字典/MutableHeaders/键值对列表/元组)
|
|
3
|
+
target_headers, # 目标headers(原有值需保留,同名覆盖source)
|
|
4
|
+
keep_keys=None, # 需保留的key集合(None表示保留所有)
|
|
5
|
+
delete_keys={'content-length', 'accept',
|
|
6
|
+
'content-type', 'sec-fetch-mode',
|
|
7
|
+
'sec-fetch-dest', 'sec-fetch-site',
|
|
8
|
+
'pragma', 'cache-control',
|
|
9
|
+
'accept-encoding', 'priority'}, # 需删除的source key集合
|
|
10
|
+
encoding='utf-8' # 字符编码(处理bytes转换)
|
|
11
|
+
) -> dict:
|
|
12
|
+
"""
|
|
13
|
+
合并headers,最终规则:
|
|
14
|
+
1. 所有key统一转为小写进行比较判断(完全大小写无关)
|
|
15
|
+
2. target_headers 同名key 完全覆盖 source_headers(source同名key不生效)
|
|
16
|
+
3. delete_keys 作用于source_headers:source中所有该列表内的key一律不添加(无论是否新增)
|
|
17
|
+
4. target_headers 中的key即使在delete_keys也始终保留,不受删除规则影响
|
|
18
|
+
5. 自动处理bytes/其他类型的键值转换为字符串
|
|
19
|
+
6. 最终输出的key全部为小写
|
|
20
|
+
"""
|
|
21
|
+
# 初始化并统一转为小写集合
|
|
22
|
+
keep_keys = {k.lower() for k in keep_keys} if keep_keys else set()
|
|
23
|
+
delete_keys = {k.lower() for k in delete_keys} if delete_keys else set()
|
|
24
|
+
|
|
25
|
+
# 修复1:兼容 MutableHeaders/普通字典/None 等 target_headers 类型
|
|
26
|
+
if target_headers is None:
|
|
27
|
+
target_dict = {}
|
|
28
|
+
elif hasattr(target_headers, 'items'):
|
|
29
|
+
# 支持 MutableHeaders/Headers/普通字典(都有items()方法)
|
|
30
|
+
target_dict = dict(target_headers.items())
|
|
31
|
+
else:
|
|
32
|
+
# 兜底:可迭代对象转为字典
|
|
33
|
+
target_dict = dict(target_headers) if isinstance(
|
|
34
|
+
target_headers, (list, tuple)) else {}
|
|
35
|
+
|
|
36
|
+
# 标准化target_headers:key转为小写,保留原有值
|
|
37
|
+
processed_headers = {k.lower(): v for k, v in target_dict.items()}
|
|
38
|
+
target_original_keys = set(processed_headers.keys())
|
|
39
|
+
|
|
40
|
+
# 修复2:统一处理 source_headers 格式,确保是键值对迭代器
|
|
41
|
+
# 步骤1:将source_headers转为标准的键值对列表
|
|
42
|
+
if source_headers is None:
|
|
43
|
+
source_kv_list = []
|
|
44
|
+
elif hasattr(source_headers, 'items'):
|
|
45
|
+
# 字典/MutableHeaders → 转为键值对列表
|
|
46
|
+
source_kv_list = list(source_headers.items())
|
|
47
|
+
elif isinstance(source_headers, (list, tuple)):
|
|
48
|
+
# 列表/元组 → 校验并过滤合法的键值对(仅保留长度为2的元组/列表)
|
|
49
|
+
source_kv_list = []
|
|
50
|
+
for item in source_headers:
|
|
51
|
+
if isinstance(item, (list, tuple)) and len(item) == 2:
|
|
52
|
+
source_kv_list.append(item)
|
|
53
|
+
else:
|
|
54
|
+
# 跳过非法格式(如长度≠2的元素),避免解包报错
|
|
55
|
+
continue
|
|
56
|
+
else:
|
|
57
|
+
# 其他类型 → 空列表(避免迭代报错)
|
|
58
|
+
source_kv_list = []
|
|
59
|
+
|
|
60
|
+
# 处理来源headers的键值转换和合并(遍历标准化后的键值对)
|
|
61
|
+
for key, value in source_kv_list:
|
|
62
|
+
# 转换key为字符串并统一转为小写(判断用)
|
|
63
|
+
if not isinstance(key, str):
|
|
64
|
+
try:
|
|
65
|
+
key = key.decode(encoding, errors='replace') if isinstance(
|
|
66
|
+
key, bytes) else str(key)
|
|
67
|
+
except Exception:
|
|
68
|
+
# 极端情况:无法转换的key直接跳过
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
key_lower = key.lower()
|
|
72
|
+
|
|
73
|
+
# 转换value为字符串
|
|
74
|
+
if not isinstance(value, str):
|
|
75
|
+
try:
|
|
76
|
+
value = value.decode(encoding, errors='replace') if isinstance(
|
|
77
|
+
value, bytes) else str(value)
|
|
78
|
+
except Exception:
|
|
79
|
+
# 无法转换的value设为空字符串
|
|
80
|
+
value = ""
|
|
81
|
+
|
|
82
|
+
# 过滤1:source的key在删除列表 → 直接跳过
|
|
83
|
+
if key_lower in delete_keys:
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
# 过滤2:仅保留指定的key(如果设置了keep_keys)
|
|
87
|
+
if keep_keys and key_lower not in keep_keys:
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
# 过滤3:target已有同名key → 直接跳过(target值覆盖source)
|
|
91
|
+
if key_lower in target_original_keys:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
# 仅添加符合条件的key-value(最终key为小写)
|
|
95
|
+
processed_headers[key_lower] = value
|
|
96
|
+
|
|
97
|
+
return processed_headers
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_header_value(headers: list, target_key: str, default=None):
|
|
101
|
+
"""
|
|
102
|
+
从列表中查找指定 header 的值
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
headers: header 列表,例如 [('Content-Type', 'application/json'), ...]
|
|
106
|
+
target_key: 要查找的 key
|
|
107
|
+
default: 如果没找到返回的默认值
|
|
108
|
+
"""
|
|
109
|
+
if not headers:
|
|
110
|
+
return default
|
|
111
|
+
|
|
112
|
+
for item in headers:
|
|
113
|
+
# 兼容 list 和 tuple,确保长度为2
|
|
114
|
+
if isinstance(item, (list, tuple)) and len(item) == 2 and item[0] == target_key:
|
|
115
|
+
return item[1]
|
|
116
|
+
|
|
117
|
+
return default
|