sycommon-python-lib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sycommon-python-lib might be problematic. Click here for more details.
- sycommon/__init__.py +0 -0
- sycommon/config/Config.py +73 -0
- sycommon/config/DatabaseConfig.py +34 -0
- sycommon/config/EmbeddingConfig.py +16 -0
- sycommon/config/LLMConfig.py +16 -0
- sycommon/config/RerankerConfig.py +13 -0
- sycommon/config/__init__.py +0 -0
- sycommon/database/database_service.py +79 -0
- sycommon/health/__init__.py +0 -0
- sycommon/health/health_check.py +17 -0
- sycommon/health/ping.py +13 -0
- sycommon/logging/__init__.py +0 -0
- sycommon/logging/kafka_log.py +551 -0
- sycommon/logging/logger_wrapper.py +19 -0
- sycommon/middleware/__init__.py +0 -0
- sycommon/middleware/context.py +3 -0
- sycommon/middleware/cors.py +14 -0
- sycommon/middleware/exception.py +85 -0
- sycommon/middleware/middleware.py +32 -0
- sycommon/middleware/monitor_memory.py +22 -0
- sycommon/middleware/timeout.py +19 -0
- sycommon/middleware/traceid.py +138 -0
- sycommon/models/__init__.py +0 -0
- sycommon/models/log.py +30 -0
- sycommon/services.py +29 -0
- sycommon/synacos/__init__.py +0 -0
- sycommon/synacos/feign.py +307 -0
- sycommon/synacos/nacos_service.py +689 -0
- sycommon/tools/__init__.py +0 -0
- sycommon/tools/snowflake.py +11 -0
- sycommon/tools/timing.py +73 -0
- sycommon_python_lib-0.1.0.dist-info/METADATA +128 -0
- sycommon_python_lib-0.1.0.dist-info/RECORD +35 -0
- sycommon_python_lib-0.1.0.dist-info/WHEEL +5 -0
- sycommon_python_lib-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import json
|
|
3
|
+
from typing import Callable, Dict, List, Optional
|
|
4
|
+
import nacos
|
|
5
|
+
import socket
|
|
6
|
+
import signal
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import yaml
|
|
10
|
+
import time
|
|
11
|
+
import atexit
|
|
12
|
+
import traceback
|
|
13
|
+
import random
|
|
14
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
15
|
+
|
|
16
|
+
from sycommon.config.Config import SingletonMeta
|
|
17
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NacosService(metaclass=SingletonMeta):
|
|
21
|
+
def __init__(self, config):
|
|
22
|
+
if config:
|
|
23
|
+
self.config = config
|
|
24
|
+
self.nacos_config = config['Nacos']
|
|
25
|
+
self.service_name = config['Name']
|
|
26
|
+
self.host = config['Host']
|
|
27
|
+
self.port = config['Port']
|
|
28
|
+
self.version = os.getenv('VERSION')
|
|
29
|
+
self.registered = False
|
|
30
|
+
self._client_initialized = False # 客户端初始化状态
|
|
31
|
+
self._shutdown_event = threading.Event()
|
|
32
|
+
self._executor = ThreadPoolExecutor(max_workers=5)
|
|
33
|
+
|
|
34
|
+
# 配置参数
|
|
35
|
+
self.max_retries = self.nacos_config.get('maxRetries', 5)
|
|
36
|
+
self.retry_delay = self.nacos_config.get('retryDelay', 1)
|
|
37
|
+
self.retry_backoff = self.nacos_config.get('retryBackoff', 1.5)
|
|
38
|
+
self.max_retry_delay = self.nacos_config.get('maxRetryDelay', 30)
|
|
39
|
+
self.heartbeat_interval = self.nacos_config.get(
|
|
40
|
+
'heartbeatInterval', 5)
|
|
41
|
+
self.register_retry_interval = self.nacos_config.get(
|
|
42
|
+
'registerRetryInterval', 5) # 注册重试间隔
|
|
43
|
+
|
|
44
|
+
# 长期重试配置
|
|
45
|
+
self.long_term_retry_delay = self.nacos_config.get(
|
|
46
|
+
'longTermRetryDelay', 30)
|
|
47
|
+
self.max_long_term_retries = self.nacos_config.get(
|
|
48
|
+
'maxLongTermRetries', -1) # -1表示无限重试
|
|
49
|
+
|
|
50
|
+
# 注册验证配置
|
|
51
|
+
self.registration_verify_count = self.nacos_config.get(
|
|
52
|
+
'registrationVerifyCount', 3) # 验证次数
|
|
53
|
+
self.registration_verify_interval = self.nacos_config.get(
|
|
54
|
+
'registrationVerifyInterval', 1) # 验证间隔
|
|
55
|
+
|
|
56
|
+
self.real_ip = self.get_service_ip(self.host)
|
|
57
|
+
self._long_term_retry_count = 0 # 长期重试计数器
|
|
58
|
+
|
|
59
|
+
# 初始化客户端(仅在首次调用时执行)
|
|
60
|
+
self._initialize_client()
|
|
61
|
+
|
|
62
|
+
# 启动时清理残留实例
|
|
63
|
+
self._cleanup_stale_instance()
|
|
64
|
+
|
|
65
|
+
self.share_configs = self.read_configs()
|
|
66
|
+
|
|
67
|
+
# 配置监听器
|
|
68
|
+
self._config_listeners = {}
|
|
69
|
+
self._config_cache = {}
|
|
70
|
+
|
|
71
|
+
# 心跳相关
|
|
72
|
+
self._last_heartbeat_time = 0
|
|
73
|
+
self._heartbeat_fail_count = 0
|
|
74
|
+
self._heartbeat_thread = None
|
|
75
|
+
|
|
76
|
+
# 启动配置监视线程
|
|
77
|
+
self._watch_thread = threading.Thread(
|
|
78
|
+
target=self._watch_configs, daemon=True)
|
|
79
|
+
self._watch_thread.start()
|
|
80
|
+
|
|
81
|
+
# 启动心跳线程(在初始化完成后立即启动)
|
|
82
|
+
self.start_heartbeat()
|
|
83
|
+
|
|
84
|
+
def _initialize_client(self):
|
|
85
|
+
"""初始化Nacos客户端(仅首次调用时执行)"""
|
|
86
|
+
if self._client_initialized:
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
for attempt in range(self.max_retries):
|
|
90
|
+
try:
|
|
91
|
+
register_ip = self.nacos_config['registerIp']
|
|
92
|
+
namespace_id = self.nacos_config['namespaceId']
|
|
93
|
+
self.nacos_client = nacos.NacosClient(
|
|
94
|
+
server_addresses=register_ip,
|
|
95
|
+
namespace=namespace_id
|
|
96
|
+
)
|
|
97
|
+
SYLogger.info("nacos:客户端初始化成功")
|
|
98
|
+
self._client_initialized = True
|
|
99
|
+
return True
|
|
100
|
+
except Exception as e:
|
|
101
|
+
delay = min(self.retry_delay * (self.retry_backoff ** attempt),
|
|
102
|
+
self.max_retry_delay)
|
|
103
|
+
SYLogger.error(
|
|
104
|
+
f"nacos:客户端初始化失败 (尝试 {attempt+1}/{self.max_retries}): {e}")
|
|
105
|
+
time.sleep(delay)
|
|
106
|
+
|
|
107
|
+
SYLogger.warning("nacos:无法连接到 Nacos 服务器,已达到最大重试次数")
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
def _cleanup_stale_instance(self):
|
|
111
|
+
"""清理可能存在的残留实例"""
|
|
112
|
+
if not self._client_initialized:
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
self.nacos_client.remove_naming_instance(
|
|
117
|
+
service_name=self.service_name,
|
|
118
|
+
ip=self.real_ip,
|
|
119
|
+
port=int(self.port),
|
|
120
|
+
cluster_name="DEFAULT"
|
|
121
|
+
)
|
|
122
|
+
SYLogger.warning(
|
|
123
|
+
f"nacos:清理残留实例: {self.real_ip}:{self.port}")
|
|
124
|
+
except Exception as e:
|
|
125
|
+
SYLogger.error(f"nacos:清理残留实例异常: {e}")
|
|
126
|
+
|
|
127
|
+
def ensure_client_connected(self, retry_once=False):
|
|
128
|
+
"""确保Nacos客户端已连接,返回连接状态"""
|
|
129
|
+
# 使用线程锁保护客户端初始化状态
|
|
130
|
+
with threading.Lock():
|
|
131
|
+
if self._client_initialized:
|
|
132
|
+
return True
|
|
133
|
+
|
|
134
|
+
SYLogger.warning("nacos:客户端未初始化,尝试连接...")
|
|
135
|
+
|
|
136
|
+
# 记录尝试次数,避免无限循环
|
|
137
|
+
attempt = 0
|
|
138
|
+
max_attempts = 2 if retry_once else self.max_retries
|
|
139
|
+
|
|
140
|
+
while attempt < max_attempts:
|
|
141
|
+
try:
|
|
142
|
+
register_ip = self.nacos_config['registerIp']
|
|
143
|
+
namespace_id = self.nacos_config['namespaceId']
|
|
144
|
+
|
|
145
|
+
# 创建新的Nacos客户端实例
|
|
146
|
+
self.nacos_client = nacos.NacosClient(
|
|
147
|
+
server_addresses=register_ip,
|
|
148
|
+
namespace=namespace_id
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# 验证客户端是否真正可用
|
|
152
|
+
connection_valid = self._verify_client_connection()
|
|
153
|
+
|
|
154
|
+
if connection_valid:
|
|
155
|
+
self._client_initialized = True
|
|
156
|
+
SYLogger.info("nacos:客户端初始化成功")
|
|
157
|
+
|
|
158
|
+
# 客户端重新连接后,检查服务注册状态
|
|
159
|
+
self.registered = self.check_service_registered()
|
|
160
|
+
return True
|
|
161
|
+
else:
|
|
162
|
+
raise ConnectionError("nacos:客户端初始化后无法验证连接")
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
attempt += 1
|
|
166
|
+
delay = min(self.retry_delay * (self.retry_backoff ** (attempt - 1)),
|
|
167
|
+
self.max_retry_delay)
|
|
168
|
+
|
|
169
|
+
SYLogger.error(
|
|
170
|
+
f"nacos:客户端初始化失败 (尝试 {attempt}/{max_attempts}): {e}")
|
|
171
|
+
time.sleep(delay)
|
|
172
|
+
|
|
173
|
+
SYLogger.error("nacos:无法连接到 Nacos 服务器,已达到最大重试次数")
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
def _verify_client_connection(self):
|
|
177
|
+
"""验证客户端是否真正连接成功"""
|
|
178
|
+
try:
|
|
179
|
+
# 使用当前服务的命名实例查询来验证连接
|
|
180
|
+
namespace_id = self.nacos_config['namespaceId']
|
|
181
|
+
self.nacos_client.list_naming_instance(
|
|
182
|
+
service_name=self.service_name,
|
|
183
|
+
cluster_name="DEFAULT",
|
|
184
|
+
namespace_id=namespace_id,
|
|
185
|
+
group_name="DEFAULT_GROUP",
|
|
186
|
+
healthy_only=True
|
|
187
|
+
)
|
|
188
|
+
return True
|
|
189
|
+
except Exception as e:
|
|
190
|
+
SYLogger.warning(f"nacos:客户端连接验证失败: {e}")
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
def check_service_registered(self):
|
|
194
|
+
"""检查服务是否已注册(基于实例列表)"""
|
|
195
|
+
if not self.ensure_client_connected():
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
namespace_id = self.nacos_config['namespaceId']
|
|
200
|
+
instances = self.nacos_client.list_naming_instance(
|
|
201
|
+
self.service_name, "DEFAULT", namespace_id, "DEFAULT_GROUP", True)
|
|
202
|
+
|
|
203
|
+
# 检查是否存在包含当前IP和端口的实例
|
|
204
|
+
for instance in instances.get('hosts', []):
|
|
205
|
+
if (instance.get('ip') == self.real_ip and
|
|
206
|
+
instance.get('port') == int(self.port)):
|
|
207
|
+
SYLogger.info(
|
|
208
|
+
f"nacos:找到已注册实例: {self.real_ip}:{self.port}")
|
|
209
|
+
return True
|
|
210
|
+
|
|
211
|
+
SYLogger.warning(f"nacos:未找到注册实例: {self.real_ip}:{self.port}")
|
|
212
|
+
return False
|
|
213
|
+
except Exception as e:
|
|
214
|
+
SYLogger.error(f"nacos:检查服务注册状态失败: {e}")
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
def verify_registration(self):
|
|
218
|
+
"""多次验证服务是否成功注册"""
|
|
219
|
+
success_count = 0
|
|
220
|
+
SYLogger.info(
|
|
221
|
+
f"nacos:开始验证服务注册状态,共验证 {self.registration_verify_count} 次")
|
|
222
|
+
|
|
223
|
+
for i in range(self.registration_verify_count):
|
|
224
|
+
if self.check_service_registered():
|
|
225
|
+
success_count += 1
|
|
226
|
+
else:
|
|
227
|
+
SYLogger.warning(f"nacos:第 {i+1} 次验证未找到注册实例")
|
|
228
|
+
|
|
229
|
+
if i < self.registration_verify_count - 1:
|
|
230
|
+
time.sleep(self.registration_verify_interval)
|
|
231
|
+
|
|
232
|
+
if success_count >= self.registration_verify_count / 2:
|
|
233
|
+
SYLogger.info(
|
|
234
|
+
f"nacos:服务注册验证成功,{success_count}/{self.registration_verify_count} 次验证通过")
|
|
235
|
+
return True
|
|
236
|
+
else:
|
|
237
|
+
SYLogger.error(
|
|
238
|
+
f"nacos:服务注册验证失败,仅 {success_count}/{self.registration_verify_count} 次验证通过")
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
def register_with_retry(self):
|
|
242
|
+
"""带重试机制的服务注册(基于实例列表检查)"""
|
|
243
|
+
retry_count = 0
|
|
244
|
+
last_error = None
|
|
245
|
+
|
|
246
|
+
# 重置注册状态,确保重新检查
|
|
247
|
+
self.registered = False
|
|
248
|
+
|
|
249
|
+
while not self.registered and (self.max_long_term_retries < 0 or retry_count < self.max_long_term_retries):
|
|
250
|
+
try:
|
|
251
|
+
# 尝试注册服务
|
|
252
|
+
register_success = self.register(force=True)
|
|
253
|
+
|
|
254
|
+
if not register_success:
|
|
255
|
+
raise RuntimeError("nacos:服务注册请求失败")
|
|
256
|
+
|
|
257
|
+
# 注册请求发送成功后,等待一小段时间让Nacos服务器处理
|
|
258
|
+
SYLogger.info(
|
|
259
|
+
f"nacos:服务注册请求已发送,等待 {self.registration_verify_interval} 秒后验证")
|
|
260
|
+
time.sleep(self.registration_verify_interval)
|
|
261
|
+
|
|
262
|
+
# 多次验证服务是否真正注册成功
|
|
263
|
+
registered = self.verify_registration()
|
|
264
|
+
self.registered = registered # <-- 确保设置注册状态
|
|
265
|
+
|
|
266
|
+
if self.registered:
|
|
267
|
+
# 注册成功后,更新客户端状态
|
|
268
|
+
self._client_initialized = True
|
|
269
|
+
|
|
270
|
+
# 注册成功后,通知心跳线程立即发送心跳
|
|
271
|
+
self._shutdown_event.set()
|
|
272
|
+
self._shutdown_event.clear()
|
|
273
|
+
|
|
274
|
+
# 注册成功后,更新监控线程的状态
|
|
275
|
+
self._long_term_retry_count = 0
|
|
276
|
+
|
|
277
|
+
SYLogger.info(f"nacos:服务注册成功并通过验证: {self.service_name}")
|
|
278
|
+
return True
|
|
279
|
+
else:
|
|
280
|
+
raise RuntimeError("nacos:服务注册验证失败")
|
|
281
|
+
|
|
282
|
+
except Exception as e:
|
|
283
|
+
last_error = str(e)
|
|
284
|
+
retry_count += 1
|
|
285
|
+
delay = min(self.register_retry_interval * (self.retry_backoff ** (retry_count - 1)),
|
|
286
|
+
self.max_retry_delay)
|
|
287
|
+
|
|
288
|
+
SYLogger.warning(
|
|
289
|
+
f"nacos:服务注册尝试 {retry_count} 失败: {last_error},{delay}秒后重试")
|
|
290
|
+
time.sleep(delay)
|
|
291
|
+
|
|
292
|
+
if last_error:
|
|
293
|
+
SYLogger.error(f"nacos:服务注册失败,最终错误: {last_error}")
|
|
294
|
+
else:
|
|
295
|
+
SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {self.service_name}")
|
|
296
|
+
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
def register(self, force=False):
|
|
300
|
+
"""注册服务到Nacos"""
|
|
301
|
+
# 使用线程锁保护注册状态
|
|
302
|
+
with threading.Lock():
|
|
303
|
+
if self.registered and not force and self.check_service_registered():
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
if self.registered and not force:
|
|
307
|
+
self.registered = False
|
|
308
|
+
SYLogger.warning("nacos:本地状态显示已注册,但Nacos中未找到服务实例,准备重新注册")
|
|
309
|
+
|
|
310
|
+
metadata = {"ignore-metrics": "true"}
|
|
311
|
+
if self.version:
|
|
312
|
+
metadata["version"] = self.version
|
|
313
|
+
|
|
314
|
+
for attempt in range(self.max_retries):
|
|
315
|
+
if not self.ensure_client_connected():
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
# 注册服务
|
|
320
|
+
self.nacos_client.add_naming_instance(
|
|
321
|
+
service_name=self.service_name,
|
|
322
|
+
ip=self.real_ip,
|
|
323
|
+
port=int(self.port),
|
|
324
|
+
metadata=metadata,
|
|
325
|
+
cluster_name="DEFAULT",
|
|
326
|
+
healthy=True,
|
|
327
|
+
ephemeral=True,
|
|
328
|
+
heartbeat_interval=self.heartbeat_interval
|
|
329
|
+
)
|
|
330
|
+
SYLogger.info(
|
|
331
|
+
f"nacos:服务 {self.service_name} 注册请求已发送: {self.real_ip}:{self.port}")
|
|
332
|
+
|
|
333
|
+
# 注册退出时的清理函数
|
|
334
|
+
if not hasattr(self, '_atexit_registered') or not self._atexit_registered:
|
|
335
|
+
atexit.register(self.deregister_service)
|
|
336
|
+
self._atexit_registered = True
|
|
337
|
+
|
|
338
|
+
return True
|
|
339
|
+
except Exception as e:
|
|
340
|
+
if "signal only works in main thread" in str(e):
|
|
341
|
+
return True
|
|
342
|
+
elif attempt < self.max_retries - 1:
|
|
343
|
+
SYLogger.warning(
|
|
344
|
+
f"nacos:服务注册失败 (尝试 {attempt+1}/{self.max_retries}): {e}")
|
|
345
|
+
time.sleep(self.retry_delay)
|
|
346
|
+
else:
|
|
347
|
+
SYLogger.error(f"nacos:服务注册失败,已达到最大重试次数: {e}")
|
|
348
|
+
return False
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def setup_nacos(config: dict):
|
|
352
|
+
"""创建并初始化Nacos管理器"""
|
|
353
|
+
instance = NacosService(config)
|
|
354
|
+
|
|
355
|
+
# 使用增强的注册重试逻辑
|
|
356
|
+
if not instance.register_with_retry():
|
|
357
|
+
# 在抛出异常前,尝试注销服务以清理状态
|
|
358
|
+
try:
|
|
359
|
+
instance.deregister_service()
|
|
360
|
+
except Exception as e:
|
|
361
|
+
SYLogger.error(f"nacos:服务注册失败后,注销服务时发生错误: {e}")
|
|
362
|
+
|
|
363
|
+
raise RuntimeError("nacos:服务注册失败,应用启动终止")
|
|
364
|
+
|
|
365
|
+
# 服务注册成功后再注册信号处理
|
|
366
|
+
signal.signal(signal.SIGTERM, instance.handle_signal)
|
|
367
|
+
signal.signal(signal.SIGINT, instance.handle_signal)
|
|
368
|
+
|
|
369
|
+
# 启动连接监控线程
|
|
370
|
+
threading.Thread(target=instance.monitor_connection,
|
|
371
|
+
daemon=True).start()
|
|
372
|
+
|
|
373
|
+
return instance
|
|
374
|
+
|
|
375
|
+
def start_heartbeat(self):
|
|
376
|
+
"""启动心跳线程"""
|
|
377
|
+
if self._heartbeat_thread and self._heartbeat_thread.is_alive():
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
self._heartbeat_thread = threading.Thread(
|
|
381
|
+
target=self._send_heartbeat_loop,
|
|
382
|
+
name="NacosHeartbeatThread",
|
|
383
|
+
daemon=True
|
|
384
|
+
)
|
|
385
|
+
self._heartbeat_thread.start()
|
|
386
|
+
SYLogger.info("nacos:心跳线程已启动")
|
|
387
|
+
|
|
388
|
+
def _send_heartbeat_loop(self):
|
|
389
|
+
"""心跳发送循环 - 独立线程,不依赖外部锁"""
|
|
390
|
+
SYLogger.info("nacos:心跳线程开始运行")
|
|
391
|
+
consecutive_failures = 0 # 连续失败次数计数器
|
|
392
|
+
last_successful_heartbeat = time.time() # 上次成功心跳时间
|
|
393
|
+
thread_start_time = time.time() # 线程启动时间
|
|
394
|
+
heartbeat_counter = 0 # 心跳计数器
|
|
395
|
+
|
|
396
|
+
# 初始化为当前时间,以便尽快发送第一次心跳
|
|
397
|
+
next_heartbeat_time = time.time()
|
|
398
|
+
|
|
399
|
+
while not self._shutdown_event.is_set():
|
|
400
|
+
try:
|
|
401
|
+
current_time = time.time()
|
|
402
|
+
SYLogger.info(
|
|
403
|
+
f"nacos:心跳线程检查状态,registered={self.registered}, 上次成功心跳: {last_successful_heartbeat}, 运行时间: {current_time-thread_start_time:.2f}s, 心跳计数: {heartbeat_counter}")
|
|
404
|
+
|
|
405
|
+
# 检查是否到了发送心跳的时间
|
|
406
|
+
if current_time >= next_heartbeat_time:
|
|
407
|
+
# 复制注册状态,减少锁的持有时间
|
|
408
|
+
is_registered = self.registered
|
|
409
|
+
|
|
410
|
+
if is_registered:
|
|
411
|
+
# 双重检查,确保服务确实注册
|
|
412
|
+
if not self.check_service_registered():
|
|
413
|
+
SYLogger.warning(
|
|
414
|
+
"nacos:服务状态显示已注册,但Nacos中未找到实例")
|
|
415
|
+
self.registered = False
|
|
416
|
+
continue
|
|
417
|
+
|
|
418
|
+
SYLogger.info("nacos:准备发送心跳...")
|
|
419
|
+
success = self.send_heartbeat()
|
|
420
|
+
if success:
|
|
421
|
+
heartbeat_counter += 1
|
|
422
|
+
consecutive_failures = 0
|
|
423
|
+
last_successful_heartbeat = current_time
|
|
424
|
+
# 更新下一次计划发送时间
|
|
425
|
+
next_heartbeat_time = current_time + self.heartbeat_interval
|
|
426
|
+
SYLogger.info(
|
|
427
|
+
f"nacos:心跳发送成功({heartbeat_counter}),下次发送时间: {next_heartbeat_time}")
|
|
428
|
+
else:
|
|
429
|
+
consecutive_failures += 1
|
|
430
|
+
# 失败时减少下次发送间隔,加快恢复
|
|
431
|
+
next_heartbeat_time = current_time + min(
|
|
432
|
+
self.heartbeat_interval,
|
|
433
|
+
self.long_term_retry_delay *
|
|
434
|
+
(self.retry_backoff **
|
|
435
|
+
(consecutive_failures - 1))
|
|
436
|
+
)
|
|
437
|
+
SYLogger.warning(
|
|
438
|
+
f"nacos:心跳发送失败,将减少下次发送间隔为 {next_heartbeat_time - current_time:.2f} 秒")
|
|
439
|
+
else:
|
|
440
|
+
SYLogger.warning("nacos:服务未注册,跳过心跳发送")
|
|
441
|
+
# 服务未注册时,尝试重新注册
|
|
442
|
+
if current_time - next_heartbeat_time > self.register_retry_interval:
|
|
443
|
+
SYLogger.info("nacos:服务未注册,尝试重新注册")
|
|
444
|
+
self.register_with_retry()
|
|
445
|
+
next_heartbeat_time = current_time + self.heartbeat_interval
|
|
446
|
+
else:
|
|
447
|
+
# 缩短检查间隔
|
|
448
|
+
next_heartbeat_time = current_time + \
|
|
449
|
+
min(self.heartbeat_interval, 5)
|
|
450
|
+
|
|
451
|
+
# 检查线程运行时间,防止线程挂起 - 统一为1小时重置
|
|
452
|
+
if current_time - thread_start_time > 3600:
|
|
453
|
+
SYLogger.info("nacos:心跳线程已运行1小时,重置内部状态")
|
|
454
|
+
thread_start_time = current_time
|
|
455
|
+
heartbeat_counter = 0
|
|
456
|
+
|
|
457
|
+
# 检查是否长时间没有成功心跳
|
|
458
|
+
if current_time - last_successful_heartbeat > self.heartbeat_interval * 3:
|
|
459
|
+
SYLogger.warning(
|
|
460
|
+
f"nacos:已超过3个心跳周期({self.heartbeat_interval*3}秒)没有成功发送心跳,尝试重新注册")
|
|
461
|
+
self.register_with_retry()
|
|
462
|
+
last_successful_heartbeat = current_time
|
|
463
|
+
|
|
464
|
+
# 计算休眠时间,避免过度循环
|
|
465
|
+
sleep_time = next_heartbeat_time - current_time
|
|
466
|
+
if sleep_time > 0:
|
|
467
|
+
SYLogger.info(f"nacos:心跳线程休眠 {sleep_time:.2f} 秒")
|
|
468
|
+
self._shutdown_event.wait(sleep_time)
|
|
469
|
+
else:
|
|
470
|
+
# 如果已经晚了,立即执行下一次循环
|
|
471
|
+
self._shutdown_event.wait(0.1)
|
|
472
|
+
|
|
473
|
+
except Exception as e:
|
|
474
|
+
SYLogger.error(f"nacos:心跳线程异常: {str(e)}")
|
|
475
|
+
traceback.print_exc()
|
|
476
|
+
# 发生异常时,增加下次发送间隔
|
|
477
|
+
next_heartbeat_time = time.time() + 5
|
|
478
|
+
self._shutdown_event.wait(1)
|
|
479
|
+
|
|
480
|
+
SYLogger.info("nacos:心跳线程已停止")
|
|
481
|
+
|
|
482
|
+
def send_heartbeat(self):
|
|
483
|
+
"""发送心跳到Nacos"""
|
|
484
|
+
if not self.ensure_client_connected():
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
try:
|
|
488
|
+
# 发送心跳
|
|
489
|
+
result = self.nacos_client.send_heartbeat(
|
|
490
|
+
service_name=self.service_name,
|
|
491
|
+
ip=self.real_ip,
|
|
492
|
+
port=int(self.port),
|
|
493
|
+
cluster_name="DEFAULT",
|
|
494
|
+
weight=1.0,
|
|
495
|
+
metadata={"version": self.version} if self.version else None
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
# 处理返回结果
|
|
499
|
+
if result and isinstance(result, dict) and result.get('lightBeatEnabled', False):
|
|
500
|
+
SYLogger.info(
|
|
501
|
+
f"nacos:心跳发送成功,Nacos返回: {result}")
|
|
502
|
+
return True
|
|
503
|
+
else:
|
|
504
|
+
SYLogger.warning(
|
|
505
|
+
f"nacos:心跳发送失败,Nacos返回: {result}")
|
|
506
|
+
return False
|
|
507
|
+
|
|
508
|
+
except Exception as e:
|
|
509
|
+
SYLogger.error(f"nacos:发送心跳时发生异常: {e}")
|
|
510
|
+
return False
|
|
511
|
+
|
|
512
|
+
def reconnect_nacos_client(self):
|
|
513
|
+
"""重新连接Nacos客户端"""
|
|
514
|
+
SYLogger.warning("nacos:尝试重新连接Nacos客户端")
|
|
515
|
+
self._client_initialized = False
|
|
516
|
+
return self.ensure_client_connected()
|
|
517
|
+
|
|
518
|
+
def monitor_connection(self):
|
|
519
|
+
"""监控Nacos连接状态,定期检查并在需要时重连"""
|
|
520
|
+
check_interval = self.nacos_config.get('checkInterval', 10)
|
|
521
|
+
thread_start_time = time.time() # 线程启动时间
|
|
522
|
+
check_counter = 0 # 检查计数器
|
|
523
|
+
|
|
524
|
+
while not self._shutdown_event.is_set():
|
|
525
|
+
try:
|
|
526
|
+
current_time = time.time()
|
|
527
|
+
SYLogger.info(
|
|
528
|
+
f"nacos:连接监控线程运行中,运行时间: {current_time-thread_start_time:.2f}s, 检查计数: {check_counter}")
|
|
529
|
+
|
|
530
|
+
# 检查客户端连接状态
|
|
531
|
+
if not self.ensure_client_connected():
|
|
532
|
+
SYLogger.warning("nacos:检测到Nacos客户端连接丢失,尝试重新初始化")
|
|
533
|
+
self._initialize_client() # 尝试重新初始化客户端
|
|
534
|
+
|
|
535
|
+
# 检查服务注册状态
|
|
536
|
+
current_registered = self.check_service_registered()
|
|
537
|
+
|
|
538
|
+
# 更新注册状态并在状态变更时通知心跳线程
|
|
539
|
+
if current_registered != self.registered:
|
|
540
|
+
if current_registered:
|
|
541
|
+
self.registered = True
|
|
542
|
+
SYLogger.info(f"nacos:服务实例已重新注册")
|
|
543
|
+
else:
|
|
544
|
+
self.registered = False
|
|
545
|
+
SYLogger.warning(f"nacos:服务实例未注册,尝试重新注册")
|
|
546
|
+
# 不在锁内调用可能耗时的操作
|
|
547
|
+
self._executor.submit(self.register_with_retry)
|
|
548
|
+
|
|
549
|
+
# 额外检查:即使状态未变,也定期验证服务是否真的可用
|
|
550
|
+
if random.random() < 0.2: # 20%的概率执行深度检查
|
|
551
|
+
self.verify_registration()
|
|
552
|
+
|
|
553
|
+
# 每小时重置一次内部状态
|
|
554
|
+
if current_time - thread_start_time > 3600:
|
|
555
|
+
SYLogger.info("nacos:连接监控线程已运行1小时,重置内部状态")
|
|
556
|
+
thread_start_time = current_time
|
|
557
|
+
check_counter = 0
|
|
558
|
+
|
|
559
|
+
check_counter += 1
|
|
560
|
+
# 休眠指定时间
|
|
561
|
+
self._shutdown_event.wait(check_interval)
|
|
562
|
+
except Exception as e:
|
|
563
|
+
SYLogger.error(f"nacos:连接监控异常: {e}")
|
|
564
|
+
time.sleep(self.retry_delay)
|
|
565
|
+
|
|
566
|
+
def get_service_ip(self, config_ip):
|
|
567
|
+
"""获取服务实际IP地址"""
|
|
568
|
+
if config_ip in ['127.0.0.1', '0.0.0.0']:
|
|
569
|
+
try:
|
|
570
|
+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
571
|
+
s.connect(('8.8.8.8', 80))
|
|
572
|
+
return s.getsockname()[0]
|
|
573
|
+
except Exception:
|
|
574
|
+
return '127.0.0.1'
|
|
575
|
+
return config_ip
|
|
576
|
+
|
|
577
|
+
def deregister_service(self):
|
|
578
|
+
"""从Nacos注销服务"""
|
|
579
|
+
if not self.registered or not self._client_initialized:
|
|
580
|
+
return
|
|
581
|
+
|
|
582
|
+
SYLogger.info("nacos:正在注销服务...")
|
|
583
|
+
try:
|
|
584
|
+
self.nacos_client.remove_naming_instance(
|
|
585
|
+
service_name=self.service_name,
|
|
586
|
+
ip=self.real_ip,
|
|
587
|
+
port=int(self.port),
|
|
588
|
+
cluster_name="DEFAULT"
|
|
589
|
+
)
|
|
590
|
+
self.registered = False
|
|
591
|
+
SYLogger.info(f"nacos:服务 {self.service_name} 已注销")
|
|
592
|
+
except Exception as e:
|
|
593
|
+
SYLogger.error(f"nacos:注销服务时发生错误: {e}")
|
|
594
|
+
finally:
|
|
595
|
+
self._shutdown_event.set()
|
|
596
|
+
self._executor.shutdown()
|
|
597
|
+
|
|
598
|
+
def handle_signal(self, signum, frame):
|
|
599
|
+
"""处理退出信号"""
|
|
600
|
+
SYLogger.info(f"nacos:收到信号 {signum},正在关闭服务...")
|
|
601
|
+
self.deregister_service()
|
|
602
|
+
sys.exit(0)
|
|
603
|
+
|
|
604
|
+
def read_configs(self) -> dict:
|
|
605
|
+
"""读取共享配置"""
|
|
606
|
+
configs = {}
|
|
607
|
+
shared_configs = self.nacos_config.get('sharedConfigs', [])
|
|
608
|
+
|
|
609
|
+
for config in shared_configs:
|
|
610
|
+
data_id = config['dataId']
|
|
611
|
+
group = config['group']
|
|
612
|
+
|
|
613
|
+
for attempt in range(self.max_retries):
|
|
614
|
+
try:
|
|
615
|
+
# 检查客户端连接
|
|
616
|
+
if not self.ensure_client_connected():
|
|
617
|
+
self.reconnect_nacos_client()
|
|
618
|
+
|
|
619
|
+
# 获取配置
|
|
620
|
+
content = self.nacos_client.get_config(data_id, group)
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
configs[data_id] = json.loads(content)
|
|
624
|
+
except json.JSONDecodeError:
|
|
625
|
+
try:
|
|
626
|
+
configs[data_id] = yaml.safe_load(content)
|
|
627
|
+
except yaml.YAMLError:
|
|
628
|
+
SYLogger.error(f"nacos:无法解析 {data_id} 的内容")
|
|
629
|
+
break
|
|
630
|
+
except Exception as e:
|
|
631
|
+
if attempt < self.max_retries - 1:
|
|
632
|
+
SYLogger.warning(
|
|
633
|
+
f"nacos:读取配置 {data_id} 失败 (尝试 {attempt+1}/{self.max_retries}): {e}")
|
|
634
|
+
time.sleep(self.retry_delay)
|
|
635
|
+
else:
|
|
636
|
+
SYLogger.error(
|
|
637
|
+
f"nacos:读取配置 {data_id} 失败,已达到最大重试次数: {e}")
|
|
638
|
+
|
|
639
|
+
return configs
|
|
640
|
+
|
|
641
|
+
def add_config_listener(self, data_id: str, callback: Callable[[str], None]):
|
|
642
|
+
"""添加配置变更监听器"""
|
|
643
|
+
self._config_listeners[data_id] = callback
|
|
644
|
+
# 初始获取一次配置
|
|
645
|
+
if config := self.get_config(data_id):
|
|
646
|
+
callback(config)
|
|
647
|
+
|
|
648
|
+
def get_config(self, data_id: str, group: str = "DEFAULT_GROUP") -> Optional[str]:
|
|
649
|
+
"""获取配置内容"""
|
|
650
|
+
if not self.ensure_client_connected():
|
|
651
|
+
return None
|
|
652
|
+
|
|
653
|
+
try:
|
|
654
|
+
return self.nacos_client.get_config(data_id, group=group)
|
|
655
|
+
except Exception as e:
|
|
656
|
+
SYLogger.error(f"nacos:获取配置 {data_id} 失败: {str(e)}")
|
|
657
|
+
return None
|
|
658
|
+
|
|
659
|
+
def _watch_configs(self):
|
|
660
|
+
"""配置监听线程"""
|
|
661
|
+
while not self._shutdown_event.is_set():
|
|
662
|
+
try:
|
|
663
|
+
for data_id, callback in list(self._config_listeners.items()):
|
|
664
|
+
new_config = self.get_config(data_id)
|
|
665
|
+
if new_config and new_config != self._config_cache.get(data_id):
|
|
666
|
+
self._executor.submit(callback, new_config)
|
|
667
|
+
self._config_cache[data_id] = new_config
|
|
668
|
+
except Exception as e:
|
|
669
|
+
SYLogger.error(f"nacos:配置监视线程异常: {str(e)}")
|
|
670
|
+
self._shutdown_event.wait(5) # 每5秒检查一次
|
|
671
|
+
|
|
672
|
+
def discover_services(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
|
|
673
|
+
"""发现服务实例列表 (与Java格式兼容)"""
|
|
674
|
+
if not self.ensure_client_connected():
|
|
675
|
+
return []
|
|
676
|
+
|
|
677
|
+
return self.get_service_instances(service_name, group, version)
|
|
678
|
+
|
|
679
|
+
def get_service_instances(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
|
|
680
|
+
try:
|
|
681
|
+
namespace_id = self.nacos_config['namespaceId']
|
|
682
|
+
instances = self.nacos_client.list_naming_instance(
|
|
683
|
+
service_name, "DEFAULT", namespace_id, group, True)
|
|
684
|
+
if not instances or 'hosts' not in instances:
|
|
685
|
+
return []
|
|
686
|
+
return instances.get('hosts', [])
|
|
687
|
+
except Exception as e:
|
|
688
|
+
SYLogger.error(f"nacos:服务发现失败: {service_name}: {str(e)}")
|
|
689
|
+
return []
|
|
File without changes
|