sycommon-python-lib 0.1.54__tar.gz → 0.1.55a0__tar.gz
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_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/PKG-INFO +2 -1
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/pyproject.toml +2 -1
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/services.py +26 -16
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/feign.py +6 -4
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/nacos_service.py +3 -0
- sycommon_python_lib-0.1.55a0/src/sycommon/tools/snowflake.py +221 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/PKG-INFO +2 -1
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/requires.txt +1 -0
- sycommon_python_lib-0.1.54/src/sycommon/tools/snowflake.py +0 -33
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/README.md +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/setup.cfg +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.55a0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,7 @@ Requires-Dist: kafka-python>=2.2.16
|
|
|
12
12
|
Requires-Dist: loguru>=0.7.3
|
|
13
13
|
Requires-Dist: mysql-connector-python>=9.5.0
|
|
14
14
|
Requires-Dist: nacos-sdk-python<3.0,>=2.0.9
|
|
15
|
+
Requires-Dist: netifaces>=0.11.0
|
|
15
16
|
Requires-Dist: pydantic>=2.12.4
|
|
16
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
17
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sycommon-python-lib"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.55-alpha"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -13,6 +13,7 @@ dependencies = [
|
|
|
13
13
|
"loguru>=0.7.3",
|
|
14
14
|
"mysql-connector-python>=9.5.0",
|
|
15
15
|
"nacos-sdk-python>=2.0.9,<3.0",
|
|
16
|
+
"netifaces>=0.11.0",
|
|
16
17
|
"pydantic>=2.12.4",
|
|
17
18
|
"python-dotenv>=1.2.1",
|
|
18
19
|
"pyyaml>=6.0.3",
|
|
@@ -92,17 +92,22 @@ class Services(metaclass=SingletonMeta):
|
|
|
92
92
|
async def combined_lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
93
93
|
# 1. 执行Services自身的初始化
|
|
94
94
|
instance = cls(config, app)
|
|
95
|
-
|
|
95
|
+
|
|
96
|
+
# 明确判断是否有有效的监听器/发送器配置
|
|
97
|
+
has_valid_listeners = bool(
|
|
96
98
|
rabbitmq_listeners and len(rabbitmq_listeners) > 0)
|
|
97
|
-
|
|
99
|
+
has_valid_senders = bool(
|
|
100
|
+
rabbitmq_senders and len(rabbitmq_senders) > 0)
|
|
98
101
|
|
|
99
102
|
try:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
# 只有存在监听器或发送器时才初始化RabbitMQService
|
|
104
|
+
if has_valid_listeners or has_valid_senders:
|
|
105
|
+
await instance._setup_mq_async(
|
|
106
|
+
rabbitmq_listeners=rabbitmq_listeners if has_valid_listeners else None,
|
|
107
|
+
rabbitmq_senders=rabbitmq_senders if has_valid_senders else None,
|
|
108
|
+
has_listeners=has_valid_listeners,
|
|
109
|
+
has_senders=has_valid_senders
|
|
110
|
+
)
|
|
106
111
|
cls._initialized = True
|
|
107
112
|
logging.info("Services初始化完成")
|
|
108
113
|
except Exception as e:
|
|
@@ -145,7 +150,12 @@ class Services(metaclass=SingletonMeta):
|
|
|
145
150
|
has_senders: bool = False,
|
|
146
151
|
):
|
|
147
152
|
"""异步设置MQ相关服务(适配单通道RabbitMQService)"""
|
|
148
|
-
#
|
|
153
|
+
# ========== 只有需要使用MQ时才初始化 ==========
|
|
154
|
+
if not (has_listeners or has_senders):
|
|
155
|
+
logging.info("无RabbitMQ监听器/发送器配置,跳过RabbitMQService初始化")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# 仅当有监听器或发送器时,才执行RabbitMQService初始化
|
|
149
159
|
RabbitMQService.init(self._config, has_listeners, has_senders)
|
|
150
160
|
|
|
151
161
|
# 优化:等待连接池“存在且初始化完成”(避免提前执行后续逻辑)
|
|
@@ -156,25 +166,25 @@ class Services(metaclass=SingletonMeta):
|
|
|
156
166
|
logging.info("等待RabbitMQ连接池初始化...")
|
|
157
167
|
await asyncio.sleep(0.5)
|
|
158
168
|
|
|
159
|
-
#
|
|
160
|
-
|
|
169
|
+
# ========== 保留原有严格的发送器/监听器初始化判断 ==========
|
|
170
|
+
# 只有配置了发送器才执行发送器初始化
|
|
171
|
+
if has_senders and rabbitmq_senders:
|
|
161
172
|
# 判断是否有监听器,如果有遍历监听器列表,队列名一样将prefetch_count属性设置到发送器对象中
|
|
162
|
-
if rabbitmq_listeners:
|
|
173
|
+
if has_listeners and rabbitmq_listeners:
|
|
163
174
|
for sender in rabbitmq_senders:
|
|
164
175
|
for listener in rabbitmq_listeners:
|
|
165
176
|
if sender.queue_name == listener.queue_name:
|
|
166
177
|
sender.prefetch_count = listener.prefetch_count
|
|
167
178
|
await self._setup_senders_async(rabbitmq_senders, has_listeners)
|
|
168
179
|
|
|
169
|
-
#
|
|
170
|
-
if rabbitmq_listeners:
|
|
180
|
+
# 只有配置了监听器才执行监听器初始化
|
|
181
|
+
if has_listeners and rabbitmq_listeners:
|
|
171
182
|
await self._setup_listeners_async(rabbitmq_listeners, has_senders)
|
|
172
183
|
|
|
173
184
|
# 验证初始化结果
|
|
174
185
|
if has_listeners:
|
|
175
186
|
# 异步获取客户端数量(适配新的RabbitMQService)
|
|
176
|
-
listener_count = len(
|
|
177
|
-
RabbitMQService._consumer_tasks)
|
|
187
|
+
listener_count = len(RabbitMQService._consumer_tasks)
|
|
178
188
|
logging.info(f"监听器初始化完成,共启动 {listener_count} 个消费者")
|
|
179
189
|
if listener_count == 0:
|
|
180
190
|
logging.warning("未成功初始化任何监听器,请检查配置或MQ服务状态")
|
|
@@ -135,13 +135,15 @@ async def _handle_feign_response(response, service_name: str, api_path: str):
|
|
|
135
135
|
"""
|
|
136
136
|
try:
|
|
137
137
|
status_code = response.status
|
|
138
|
-
content_type = response.headers.get('Content-Type', '')
|
|
138
|
+
content_type = response.headers.get('Content-Type', '')
|
|
139
|
+
content_type = content_type.lower() if content_type else ''
|
|
140
|
+
|
|
139
141
|
response_body = None
|
|
140
142
|
|
|
141
143
|
if status_code == 200:
|
|
142
|
-
if 'application/json' in content_type:
|
|
144
|
+
if content_type and 'application/json' in content_type:
|
|
143
145
|
response_body = await response.json()
|
|
144
|
-
elif 'text/' in content_type:
|
|
146
|
+
elif content_type and 'text/' in content_type:
|
|
145
147
|
# 文本类型(text/plain、text/html等):按文本读取
|
|
146
148
|
try:
|
|
147
149
|
response_body = await response.text(encoding='utf-8')
|
|
@@ -158,7 +160,7 @@ async def _handle_feign_response(response, service_name: str, api_path: str):
|
|
|
158
160
|
else:
|
|
159
161
|
# 非200状态:统一读取响应体(兼容文本/二进制错误信息)
|
|
160
162
|
try:
|
|
161
|
-
if 'application/json' in content_type:
|
|
163
|
+
if content_type and 'application/json' in content_type:
|
|
162
164
|
response_body = await response.json()
|
|
163
165
|
else:
|
|
164
166
|
response_body = await response.text(encoding='utf-8', errors='ignore')
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/nacos_service.py
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import threading
|
|
2
3
|
import json
|
|
3
4
|
from typing import Callable, Dict, List, Optional
|
|
@@ -14,6 +15,8 @@ import random
|
|
|
14
15
|
from sycommon.config.Config import SingletonMeta
|
|
15
16
|
from sycommon.logging.kafka_log import SYLogger
|
|
16
17
|
|
|
18
|
+
logging.getLogger("nacos.client").setLevel(logging.WARNING)
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
class NacosService(metaclass=SingletonMeta):
|
|
19
22
|
def __init__(self, config):
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import threading
|
|
3
|
+
import socket
|
|
4
|
+
import hashlib
|
|
5
|
+
import random
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from os import environ
|
|
8
|
+
import netifaces
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Snowflake:
|
|
12
|
+
"""雪花算法生成器(无公网依赖,适配内网环境)"""
|
|
13
|
+
START_TIMESTAMP = 1388534400000 # 2014-01-01 00:00:00
|
|
14
|
+
SEQUENCE_BITS = 12
|
|
15
|
+
MACHINE_ID_BITS = 10
|
|
16
|
+
MAX_MACHINE_ID = (1 << MACHINE_ID_BITS) - 1 # 0~1023
|
|
17
|
+
MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1
|
|
18
|
+
MACHINE_ID_SHIFT = SEQUENCE_BITS
|
|
19
|
+
TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS
|
|
20
|
+
|
|
21
|
+
# 类级别的单例实例
|
|
22
|
+
_instance = None
|
|
23
|
+
_instance_lock = threading.Lock()
|
|
24
|
+
|
|
25
|
+
def __init__(self, machine_id: Optional[int] = None):
|
|
26
|
+
"""
|
|
27
|
+
初始化:优先使用传入的machine_id,否则自动从K8s环境获取
|
|
28
|
+
:param machine_id: 手动指定机器ID(None则自动计算)
|
|
29
|
+
"""
|
|
30
|
+
# 自动计算K8s环境下的machine_id
|
|
31
|
+
if machine_id is None:
|
|
32
|
+
machine_id = self._get_k8s_machine_id()
|
|
33
|
+
|
|
34
|
+
if not (0 <= machine_id <= self.MAX_MACHINE_ID):
|
|
35
|
+
raise ValueError(f"机器ID必须在0~{self.MAX_MACHINE_ID}之间")
|
|
36
|
+
|
|
37
|
+
self.machine_id = machine_id
|
|
38
|
+
self.last_timestamp = -1
|
|
39
|
+
self.sequence = 0
|
|
40
|
+
self.lock = threading.Lock()
|
|
41
|
+
|
|
42
|
+
def _get_k8s_machine_id(self) -> int:
|
|
43
|
+
"""
|
|
44
|
+
从K8s环境自动计算唯一machine_id(无公网依赖,多层兜底):
|
|
45
|
+
优先级:POD_NAME > POD_IP > 容器内网IP(网卡读取) > 容器主机名 > 随机数(最终兜底)
|
|
46
|
+
"""
|
|
47
|
+
# 1. 优先读取K8s内置的POD_NAME(默认注入,优先级最高)
|
|
48
|
+
pod_name = environ.get("POD_NAME")
|
|
49
|
+
if pod_name:
|
|
50
|
+
return self._hash_to_machine_id(pod_name)
|
|
51
|
+
|
|
52
|
+
# 2. 读取POD_IP(手动配置downwardAPI后必存在)
|
|
53
|
+
pod_ip = environ.get("POD_IP")
|
|
54
|
+
if pod_ip:
|
|
55
|
+
return self._hash_to_machine_id(pod_ip)
|
|
56
|
+
|
|
57
|
+
# 3. 兜底1:读取本机网卡获取内网IP(无公网依赖)
|
|
58
|
+
try:
|
|
59
|
+
local_ip = self._get_local_internal_ip()
|
|
60
|
+
if local_ip:
|
|
61
|
+
return self._hash_to_machine_id(local_ip)
|
|
62
|
+
else:
|
|
63
|
+
# logger.warning("读取网卡信息成功,但未找到非回环内网IP")
|
|
64
|
+
pass
|
|
65
|
+
except Exception as e:
|
|
66
|
+
# logger.warning(f"读取本机网卡IP失败: {e},尝试使用主机名")
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# 4. 兜底2:获取容器主机名(K8s中默认等于Pod名称,保证唯一)
|
|
70
|
+
hostname = socket.gethostname()
|
|
71
|
+
if hostname:
|
|
72
|
+
# logger.info(
|
|
73
|
+
# f"未读取到POD_NAME/POD_IP/内网IP,使用主机名: {hostname}生成machine_id")
|
|
74
|
+
return self._hash_to_machine_id(hostname)
|
|
75
|
+
|
|
76
|
+
# 5. 最终兜底:生成随机数(仅极端情况使用,日志告警)
|
|
77
|
+
random_id = random.randint(0, self.MAX_MACHINE_ID)
|
|
78
|
+
# logger.warning(f"所有方式均失败,使用随机数生成machine_id: {random_id}(可能重复!)")
|
|
79
|
+
return random_id
|
|
80
|
+
|
|
81
|
+
def _get_local_internal_ip(self) -> Optional[str]:
|
|
82
|
+
"""
|
|
83
|
+
读取本机网卡信息,获取非回环的内网IP(无公网依赖)
|
|
84
|
+
:return: 内网IP字符串,失败返回None
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
# 遍历所有网卡
|
|
88
|
+
for interface in netifaces.interfaces():
|
|
89
|
+
# 获取网卡的IP地址信息
|
|
90
|
+
addrs = netifaces.ifaddresses(interface)
|
|
91
|
+
# 只取IPv4地址
|
|
92
|
+
if netifaces.AF_INET in addrs:
|
|
93
|
+
for addr in addrs[netifaces.AF_INET]:
|
|
94
|
+
ip = addr.get('addr')
|
|
95
|
+
# 过滤回环地址(127.0.0.1)和docker虚拟地址(172.17.0.0/16可选过滤)
|
|
96
|
+
if ip and not ip.startswith('127.'):
|
|
97
|
+
# 可选:过滤docker0的默认地址段(根据实际内网段调整)
|
|
98
|
+
# if not ip.startswith('172.17.'):
|
|
99
|
+
return ip
|
|
100
|
+
return None
|
|
101
|
+
except ImportError:
|
|
102
|
+
# 若未安装netifaces,降级为socket方式(仅尝试本地解析,无公网连接)
|
|
103
|
+
# logger.warning("未安装netifaces库,尝试降级方式获取IP")
|
|
104
|
+
return self._get_local_ip_fallback()
|
|
105
|
+
|
|
106
|
+
def _get_local_ip_fallback(self) -> Optional[str]:
|
|
107
|
+
"""
|
|
108
|
+
降级方案:不连接公网,仅通过本地socket获取IP(兼容无netifaces的场景)
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
# 创建socket但不连接任何地址,仅绑定到本地
|
|
112
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
113
|
+
# 绑定到0.0.0.0:0(仅用于获取本机IP,不发送数据)
|
|
114
|
+
s.bind(('', 0))
|
|
115
|
+
local_ip = s.getsockname()[0]
|
|
116
|
+
s.close()
|
|
117
|
+
# 过滤回环地址
|
|
118
|
+
if not local_ip.startswith('127.'):
|
|
119
|
+
return local_ip
|
|
120
|
+
return None
|
|
121
|
+
except Exception:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def _hash_to_machine_id(self, text: str) -> int:
|
|
125
|
+
"""将字符串哈希后取模,得到0~1023的machine_id(保证分布均匀)"""
|
|
126
|
+
hash_bytes = hashlib.md5(text.encode("utf-8")).digest()
|
|
127
|
+
hash_int = int.from_bytes(hash_bytes[:4], byteorder="big")
|
|
128
|
+
return hash_int % self.MAX_MACHINE_ID
|
|
129
|
+
|
|
130
|
+
def _get_current_timestamp(self) -> int:
|
|
131
|
+
return int(time.time() * 1000)
|
|
132
|
+
|
|
133
|
+
def _wait_next_millisecond(self, current_timestamp: int) -> int:
|
|
134
|
+
while current_timestamp <= self.last_timestamp:
|
|
135
|
+
current_timestamp = self._get_current_timestamp()
|
|
136
|
+
return current_timestamp
|
|
137
|
+
|
|
138
|
+
def generate_id(self) -> int:
|
|
139
|
+
with self.lock:
|
|
140
|
+
current_timestamp = self._get_current_timestamp()
|
|
141
|
+
|
|
142
|
+
if current_timestamp < self.last_timestamp:
|
|
143
|
+
raise RuntimeError(
|
|
144
|
+
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp})"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if current_timestamp == self.last_timestamp:
|
|
148
|
+
self.sequence = (self.sequence + 1) & self.MAX_SEQUENCE
|
|
149
|
+
if self.sequence == 0:
|
|
150
|
+
current_timestamp = self._wait_next_millisecond(
|
|
151
|
+
current_timestamp)
|
|
152
|
+
else:
|
|
153
|
+
self.sequence = 0
|
|
154
|
+
|
|
155
|
+
self.last_timestamp = current_timestamp
|
|
156
|
+
|
|
157
|
+
snowflake_id = (
|
|
158
|
+
((current_timestamp - self.START_TIMESTAMP) << self.TIMESTAMP_SHIFT)
|
|
159
|
+
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
160
|
+
| self.sequence
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return snowflake_id
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def parse_id(snowflake_id: int) -> dict:
|
|
167
|
+
from datetime import datetime
|
|
168
|
+
sequence = snowflake_id & Snowflake.MAX_SEQUENCE
|
|
169
|
+
machine_id = (snowflake_id >>
|
|
170
|
+
Snowflake.MACHINE_ID_SHIFT) & Snowflake.MAX_MACHINE_ID
|
|
171
|
+
timestamp = (snowflake_id >> Snowflake.TIMESTAMP_SHIFT) + \
|
|
172
|
+
Snowflake.START_TIMESTAMP
|
|
173
|
+
generate_time = datetime.fromtimestamp(
|
|
174
|
+
timestamp / 1000).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
"snowflake_id": snowflake_id,
|
|
178
|
+
"generate_time": generate_time,
|
|
179
|
+
"machine_id": machine_id,
|
|
180
|
+
"sequence": sequence
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def next_id(cls) -> str:
|
|
185
|
+
"""
|
|
186
|
+
生成雪花ID(单例模式,避免重复创建实例)
|
|
187
|
+
:return: 雪花ID字符串
|
|
188
|
+
"""
|
|
189
|
+
# 单例模式创建实例
|
|
190
|
+
if cls._instance is None:
|
|
191
|
+
with cls._instance_lock:
|
|
192
|
+
if cls._instance is None:
|
|
193
|
+
cls._instance = cls()
|
|
194
|
+
# 生成ID并转为字符串返回
|
|
195
|
+
return str(cls._instance.generate_id())
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
# 生成1000个ID并验证
|
|
200
|
+
id_set = set() # 用于检测重复ID
|
|
201
|
+
_MAX_JAVA_LONG = 9223372036854775807
|
|
202
|
+
|
|
203
|
+
for i in range(1000):
|
|
204
|
+
id_str = Snowflake.next_id()
|
|
205
|
+
id_num = int(id_str)
|
|
206
|
+
|
|
207
|
+
# 验证ID不超过Java long最大值
|
|
208
|
+
assert id_num <= _MAX_JAVA_LONG, f"ID超过Java long最大值: {id_num}"
|
|
209
|
+
|
|
210
|
+
# 验证ID不重复
|
|
211
|
+
assert id_str not in id_set, f"重复生成ID: {id_str}"
|
|
212
|
+
id_set.add(id_str)
|
|
213
|
+
|
|
214
|
+
# 每100个ID打印一次解析结果
|
|
215
|
+
if i % 100 == 0:
|
|
216
|
+
parse_result = Snowflake.parse_id(id_num)
|
|
217
|
+
print(f"生成ID: {id_str}")
|
|
218
|
+
print(f"解析结果: {parse_result}")
|
|
219
|
+
print("-" * 50)
|
|
220
|
+
|
|
221
|
+
print(f"成功生成{len(id_set)}个唯一雪花ID,验证通过!")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.55a0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,7 @@ Requires-Dist: kafka-python>=2.2.16
|
|
|
12
12
|
Requires-Dist: loguru>=0.7.3
|
|
13
13
|
Requires-Dist: mysql-connector-python>=9.5.0
|
|
14
14
|
Requires-Dist: nacos-sdk-python<3.0,>=2.0.9
|
|
15
|
+
Requires-Dist: netifaces>=0.11.0
|
|
15
16
|
Requires-Dist: pydantic>=2.12.4
|
|
16
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
17
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Snowflake:
|
|
5
|
-
"""基于UUID生成兼容Java long类型的唯一ID"""
|
|
6
|
-
_MAX_JAVA_LONG = 9223372036854775807 # Java long最大值(18位)
|
|
7
|
-
|
|
8
|
-
@staticmethod
|
|
9
|
-
def next_id() -> str:
|
|
10
|
-
"""生成不超过Java long最大值的唯一ID字符串(18位以内)"""
|
|
11
|
-
while True:
|
|
12
|
-
# 生成UUID并转换为整数
|
|
13
|
-
uuid_int = int(uuid.uuid4().hex, 16)
|
|
14
|
-
|
|
15
|
-
# 取低63位,并强制限制不超过Java long最大值
|
|
16
|
-
id_num = uuid_int & ((1 << 63) - 1) # 取低63位
|
|
17
|
-
id_num = min(id_num, Snowflake._MAX_JAVA_LONG) # 强制限制最大值
|
|
18
|
-
|
|
19
|
-
# 转换为字符串并验证长度(确保18位以内)
|
|
20
|
-
id_str = str(id_num)
|
|
21
|
-
if len(id_str) <= 18:
|
|
22
|
-
return id_str
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# 使用示例
|
|
26
|
-
if __name__ == "__main__":
|
|
27
|
-
# 生成1000个ID并验证
|
|
28
|
-
for _ in range(1000):
|
|
29
|
-
id_str = Snowflake.next_id()
|
|
30
|
-
id_num = int(id_str)
|
|
31
|
-
print(f"ID: {id_str} (长度: {len(id_str)})")
|
|
32
|
-
assert len(id_str) <= 18, f"ID长度超过18位: {id_str}"
|
|
33
|
-
assert id_num <= Snowflake._MAX_JAVA_LONG, f"ID超过Java long最大值: {id_num}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/DatabaseConfig.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/EmbeddingConfig.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/LLMConfig.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/config/RerankerConfig.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/database/base_db_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/health/health_check.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/__init__.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/kafka_log.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/logger_wrapper.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/logging/sql_logger.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/__init__.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/exception.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/timeout.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/middleware/traceid.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/base_http.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqlistener_config.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqmsg_model.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/models/mqsend_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/rabbitmq/rabbitmq_client.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/rabbitmq/rabbitmq_pool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/example2.py
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.54 → sycommon_python_lib-0.1.55a0}/src/sycommon/synacos/feign_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|