sycommon-python-lib 0.1.55__py3-none-any.whl → 0.1.55a0__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/logging/kafka_log.py +9 -21
- sycommon/middleware/context.py +0 -2
- sycommon/middleware/traceid.py +32 -155
- sycommon/services.py +51 -54
- sycommon/synacos/feign.py +4 -9
- sycommon/synacos/feign_client.py +10 -24
- sycommon/synacos/nacos_service.py +2 -0
- sycommon/tools/snowflake.py +81 -160
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55a0.dist-info}/METADATA +9 -9
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55a0.dist-info}/RECORD +13 -18
- sycommon/database/async_base_db_service.py +0 -36
- sycommon/database/async_database_service.py +0 -96
- sycommon/logging/async_sql_logger.py +0 -65
- sycommon/logging/logger_levels.py +0 -23
- sycommon/tools/merge_headers.py +0 -97
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55a0.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55a0.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55a0.dist-info}/top_level.txt +0 -0
sycommon/synacos/feign.py
CHANGED
|
@@ -2,7 +2,6 @@ import io
|
|
|
2
2
|
import os
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
|
-
from sycommon.tools.merge_headers import merge_headers
|
|
6
5
|
from sycommon.tools.snowflake import Snowflake
|
|
7
6
|
|
|
8
7
|
import aiohttp
|
|
@@ -25,18 +24,14 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
|
|
|
25
24
|
try:
|
|
26
25
|
# 初始化headers,确保是可修改的字典
|
|
27
26
|
headers = headers.copy() if headers else {}
|
|
28
|
-
headers = merge_headers(SYLogger.get_headers(), headers)
|
|
29
27
|
if "x-traceId-header" not in headers:
|
|
30
|
-
headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.
|
|
28
|
+
headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.next_id()
|
|
31
29
|
|
|
32
30
|
# 处理JSON请求的Content-Type
|
|
33
31
|
is_json_request = method.upper() in ["POST", "PUT", "PATCH"] and not (
|
|
34
32
|
files or form_data or file_path)
|
|
35
|
-
if is_json_request:
|
|
36
|
-
|
|
37
|
-
headers_lower = {k.lower(): v for k, v in headers.items()}
|
|
38
|
-
if "content-type" not in headers_lower:
|
|
39
|
-
headers["Content-Type"] = "application/json"
|
|
33
|
+
if is_json_request and "Content-Type" not in headers:
|
|
34
|
+
headers["Content-Type"] = "application/json"
|
|
40
35
|
|
|
41
36
|
nacos_service = NacosService(None)
|
|
42
37
|
version = headers.get('s-y-version')
|
|
@@ -52,7 +47,7 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
|
|
|
52
47
|
instance = instances[int(time.time()) % len(instances)]
|
|
53
48
|
|
|
54
49
|
SYLogger.info(f"nacos:开始调用服务: {service_name}")
|
|
55
|
-
|
|
50
|
+
SYLogger.info(f"nacos:请求头: {headers}")
|
|
56
51
|
|
|
57
52
|
ip = instance.get('ip')
|
|
58
53
|
port = instance.get('port')
|
sycommon/synacos/feign_client.py
CHANGED
|
@@ -5,7 +5,6 @@ import inspect
|
|
|
5
5
|
from typing import Any, Dict, Optional, Literal, Type, TypeVar
|
|
6
6
|
from urllib.parse import urljoin
|
|
7
7
|
|
|
8
|
-
from sycommon.tools.merge_headers import merge_headers
|
|
9
8
|
from sycommon.tools.snowflake import Snowflake
|
|
10
9
|
|
|
11
10
|
import aiohttp
|
|
@@ -29,9 +28,7 @@ def feign_client(
|
|
|
29
28
|
default_headers: Optional[Dict[str, str]] = None
|
|
30
29
|
):
|
|
31
30
|
default_headers = default_headers or {}
|
|
32
|
-
default_headers =
|
|
33
|
-
default_headers = merge_headers(SYLogger.get_headers(), default_headers)
|
|
34
|
-
default_headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.id
|
|
31
|
+
default_headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.next_id()
|
|
35
32
|
|
|
36
33
|
def decorator(cls):
|
|
37
34
|
class FeignClient:
|
|
@@ -39,8 +36,7 @@ def feign_client(
|
|
|
39
36
|
self.service_name = service_name
|
|
40
37
|
self.path_prefix = path_prefix
|
|
41
38
|
self.default_timeout = default_timeout
|
|
42
|
-
self.default_headers =
|
|
43
|
-
k.lower(): v for k, v in default_headers.copy().items()}
|
|
39
|
+
self.default_headers = default_headers.copy()
|
|
44
40
|
self.nacos_manager: Optional[NacosService] = None
|
|
45
41
|
self.session: Optional[aiohttp.ClientSession] = None
|
|
46
42
|
|
|
@@ -69,8 +65,7 @@ def feign_client(
|
|
|
69
65
|
method = request_meta.get("method", "GET").upper()
|
|
70
66
|
path = request_meta.get("path", "")
|
|
71
67
|
is_upload = request_meta.get("is_upload", False)
|
|
72
|
-
method_headers = {
|
|
73
|
-
k.lower(): v for k, v in request_meta.get("headers", {}).items()}
|
|
68
|
+
method_headers = request_meta.get("headers", {})
|
|
74
69
|
timeout = request_meta.get(
|
|
75
70
|
"timeout", self.default_timeout)
|
|
76
71
|
|
|
@@ -160,16 +155,12 @@ def feign_client(
|
|
|
160
155
|
def _build_headers(self, param_meta: Dict[str, Param], bound_args: Dict[str, Any], method_headers: Dict[str, str]) -> Dict[str, str]:
|
|
161
156
|
headers = self.default_headers.copy()
|
|
162
157
|
headers.update(method_headers)
|
|
163
|
-
headers =
|
|
164
|
-
headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.id
|
|
165
|
-
|
|
166
|
-
# 处理参数中的Header类型
|
|
158
|
+
headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.next_id()
|
|
167
159
|
for name, meta in param_meta.items():
|
|
168
160
|
if isinstance(meta, Header) and name in bound_args:
|
|
169
161
|
value = bound_args[name]
|
|
170
162
|
if value is not None:
|
|
171
|
-
|
|
172
|
-
headers[header_key] = str(value)
|
|
163
|
+
headers[meta.get_key(name)] = str(value)
|
|
173
164
|
return headers
|
|
174
165
|
|
|
175
166
|
def _replace_path_params(self, path: str, param_meta: Dict[str, Param], bound_args: Dict[str, Any]) -> str:
|
|
@@ -234,14 +225,10 @@ def feign_client(
|
|
|
234
225
|
value) if not isinstance(value, dict) else value)
|
|
235
226
|
return form_data
|
|
236
227
|
|
|
237
|
-
# 从headers中获取Content-Type(已小写key)
|
|
238
|
-
content_type = self.default_headers.get(
|
|
239
|
-
"content-type") or method_headers.get("content-type", "")
|
|
240
|
-
# 转为小写进行判断
|
|
241
|
-
content_type_lower = content_type.lower()
|
|
242
|
-
|
|
243
228
|
# 处理表单提交(x-www-form-urlencoded)
|
|
244
|
-
|
|
229
|
+
content_type = self.default_headers.get(
|
|
230
|
+
"Content-Type") or method_headers.get("Content-Type", "")
|
|
231
|
+
if "application/x-www-form-urlencoded" in content_type:
|
|
245
232
|
form_data = {}
|
|
246
233
|
for name, value in bound_args.items():
|
|
247
234
|
meta = param_meta.get(name)
|
|
@@ -287,8 +274,7 @@ def feign_client(
|
|
|
287
274
|
"""处理响应(支持 Pydantic 模型解析)"""
|
|
288
275
|
status = response.status
|
|
289
276
|
if 200 <= status < 300:
|
|
290
|
-
content_type = response.headers.get(
|
|
291
|
-
"content-type", "").lower()
|
|
277
|
+
content_type = response.headers.get("Content-Type", "")
|
|
292
278
|
if "application/json" in content_type:
|
|
293
279
|
json_data = await response.json()
|
|
294
280
|
# 若指定了 Pydantic 响应模型,自动解析
|
|
@@ -317,7 +303,7 @@ def feign_request(
|
|
|
317
303
|
func._feign_meta = {
|
|
318
304
|
"method": method.upper(),
|
|
319
305
|
"path": path,
|
|
320
|
-
"headers":
|
|
306
|
+
"headers": headers.copy() if headers else {},
|
|
321
307
|
"is_upload": False,
|
|
322
308
|
"timeout": timeout
|
|
323
309
|
}
|
|
@@ -15,6 +15,8 @@ import random
|
|
|
15
15
|
from sycommon.config.Config import SingletonMeta
|
|
16
16
|
from sycommon.logging.kafka_log import SYLogger
|
|
17
17
|
|
|
18
|
+
logging.getLogger("nacos.client").setLevel(logging.WARNING)
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
class NacosService(metaclass=SingletonMeta):
|
|
20
22
|
def __init__(self, config):
|
sycommon/tools/snowflake.py
CHANGED
|
@@ -3,29 +3,13 @@ import threading
|
|
|
3
3
|
import socket
|
|
4
4
|
import hashlib
|
|
5
5
|
import random
|
|
6
|
-
import
|
|
7
|
-
from typing import Optional, Type, Any
|
|
6
|
+
from typing import Optional
|
|
8
7
|
from os import environ
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ClassProperty:
|
|
13
|
-
"""
|
|
14
|
-
自定义类属性描述符,替代 @classmethod + @property 的废弃写法
|
|
15
|
-
支持通过 类.属性 的方式访问,无需实例化
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self, func):
|
|
19
|
-
self.func = func
|
|
20
|
-
|
|
21
|
-
def __get__(self, instance: Any, cls: Type) -> str:
|
|
22
|
-
# 调用传入的函数,并传入类本身作为第一个参数
|
|
23
|
-
return self.func(cls)
|
|
8
|
+
import netifaces
|
|
24
9
|
|
|
25
10
|
|
|
26
11
|
class Snowflake:
|
|
27
|
-
"""
|
|
28
|
-
# 基础配置(可根据业务调整)
|
|
12
|
+
"""雪花算法生成器(无公网依赖,适配内网环境)"""
|
|
29
13
|
START_TIMESTAMP = 1388534400000 # 2014-01-01 00:00:00
|
|
30
14
|
SEQUENCE_BITS = 12
|
|
31
15
|
MACHINE_ID_BITS = 10
|
|
@@ -33,10 +17,8 @@ class Snowflake:
|
|
|
33
17
|
MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1
|
|
34
18
|
MACHINE_ID_SHIFT = SEQUENCE_BITS
|
|
35
19
|
TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS
|
|
36
|
-
CLOCK_BACKWARD_THRESHOLD = 5 # 容忍的时钟回拨阈值(毫秒)
|
|
37
|
-
_MAX_JAVA_LONG = 9223372036854775807 # Java Long最大值
|
|
38
20
|
|
|
39
|
-
#
|
|
21
|
+
# 类级别的单例实例
|
|
40
22
|
_instance = None
|
|
41
23
|
_instance_lock = threading.Lock()
|
|
42
24
|
|
|
@@ -45,38 +27,22 @@ class Snowflake:
|
|
|
45
27
|
初始化:优先使用传入的machine_id,否则自动从K8s环境获取
|
|
46
28
|
:param machine_id: 手动指定机器ID(None则自动计算)
|
|
47
29
|
"""
|
|
48
|
-
# 前置校验:确保雪花ID不会超过Java Long最大值
|
|
49
|
-
self._validate_timestamp_range()
|
|
50
|
-
|
|
51
30
|
# 自动计算K8s环境下的machine_id
|
|
52
31
|
if machine_id is None:
|
|
53
32
|
machine_id = self._get_k8s_machine_id()
|
|
54
33
|
|
|
55
|
-
# 校验machine_id合法性
|
|
56
34
|
if not (0 <= machine_id <= self.MAX_MACHINE_ID):
|
|
57
35
|
raise ValueError(f"机器ID必须在0~{self.MAX_MACHINE_ID}之间")
|
|
58
36
|
|
|
59
|
-
# 初始化核心参数
|
|
60
37
|
self.machine_id = machine_id
|
|
61
38
|
self.last_timestamp = -1
|
|
62
39
|
self.sequence = 0
|
|
63
40
|
self.lock = threading.Lock()
|
|
64
41
|
|
|
65
|
-
def _validate_timestamp_range(self):
|
|
66
|
-
"""校验当前时间戳是否在雪花ID支持的范围内,避免超过Java Long最大值"""
|
|
67
|
-
max_support_timestamp = self.START_TIMESTAMP + \
|
|
68
|
-
(1 << (64 - self.TIMESTAMP_SHIFT)) - 1
|
|
69
|
-
current_timestamp = self._get_current_timestamp()
|
|
70
|
-
if current_timestamp > max_support_timestamp:
|
|
71
|
-
raise RuntimeError(
|
|
72
|
-
f"当前时间戳({current_timestamp})超过雪花ID支持的最大时间戳({max_support_timestamp}),"
|
|
73
|
-
f"请调整START_TIMESTAMP或减少TIMESTAMP_SHIFT位数"
|
|
74
|
-
)
|
|
75
|
-
|
|
76
42
|
def _get_k8s_machine_id(self) -> int:
|
|
77
43
|
"""
|
|
78
|
-
从K8s环境自动计算唯一machine_id
|
|
79
|
-
优先级:POD_NAME > POD_IP > 容器内网IP
|
|
44
|
+
从K8s环境自动计算唯一machine_id(无公网依赖,多层兜底):
|
|
45
|
+
优先级:POD_NAME > POD_IP > 容器内网IP(网卡读取) > 容器主机名 > 随机数(最终兜底)
|
|
80
46
|
"""
|
|
81
47
|
# 1. 优先读取K8s内置的POD_NAME(默认注入,优先级最高)
|
|
82
48
|
pod_name = environ.get("POD_NAME")
|
|
@@ -88,73 +54,72 @@ class Snowflake:
|
|
|
88
54
|
if pod_ip:
|
|
89
55
|
return self._hash_to_machine_id(pod_ip)
|
|
90
56
|
|
|
91
|
-
# 3. 兜底1:读取本机网卡获取内网IP
|
|
57
|
+
# 3. 兜底1:读取本机网卡获取内网IP(无公网依赖)
|
|
92
58
|
try:
|
|
93
59
|
local_ip = self._get_local_internal_ip()
|
|
94
60
|
if local_ip:
|
|
95
61
|
return self._hash_to_machine_id(local_ip)
|
|
96
|
-
|
|
62
|
+
else:
|
|
63
|
+
# logger.warning("读取网卡信息成功,但未找到非回环内网IP")
|
|
64
|
+
pass
|
|
65
|
+
except Exception as e:
|
|
66
|
+
# logger.warning(f"读取本机网卡IP失败: {e},尝试使用主机名")
|
|
97
67
|
pass
|
|
98
68
|
|
|
99
69
|
# 4. 兜底2:获取容器主机名(K8s中默认等于Pod名称,保证唯一)
|
|
100
70
|
hostname = socket.gethostname()
|
|
101
71
|
if hostname:
|
|
72
|
+
# logger.info(
|
|
73
|
+
# f"未读取到POD_NAME/POD_IP/内网IP,使用主机名: {hostname}生成machine_id")
|
|
102
74
|
return self._hash_to_machine_id(hostname)
|
|
103
75
|
|
|
104
|
-
# 5.
|
|
105
|
-
|
|
106
|
-
|
|
76
|
+
# 5. 最终兜底:生成随机数(仅极端情况使用,日志告警)
|
|
77
|
+
random_id = random.randint(0, self.MAX_MACHINE_ID)
|
|
78
|
+
# logger.warning(f"所有方式均失败,使用随机数生成machine_id: {random_id}(可能重复!)")
|
|
79
|
+
return random_id
|
|
107
80
|
|
|
108
81
|
def _get_local_internal_ip(self) -> Optional[str]:
|
|
109
82
|
"""
|
|
110
|
-
|
|
83
|
+
读取本机网卡信息,获取非回环的内网IP(无公网依赖)
|
|
111
84
|
:return: 内网IP字符串,失败返回None
|
|
112
85
|
"""
|
|
113
86
|
try:
|
|
114
|
-
#
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if addr.family == psutil.AF_INET:
|
|
124
|
-
ip = addr.address
|
|
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可选过滤)
|
|
125
96
|
if ip and not ip.startswith('127.'):
|
|
97
|
+
# 可选:过滤docker0的默认地址段(根据实际内网段调整)
|
|
98
|
+
# if not ip.startswith('172.17.'):
|
|
126
99
|
return ip
|
|
127
100
|
return None
|
|
128
|
-
except
|
|
129
|
-
#
|
|
101
|
+
except ImportError:
|
|
102
|
+
# 若未安装netifaces,降级为socket方式(仅尝试本地解析,无公网连接)
|
|
103
|
+
# logger.warning("未安装netifaces库,尝试降级方式获取IP")
|
|
130
104
|
return self._get_local_ip_fallback()
|
|
131
105
|
|
|
132
106
|
def _get_local_ip_fallback(self) -> Optional[str]:
|
|
133
107
|
"""
|
|
134
|
-
|
|
108
|
+
降级方案:不连接公网,仅通过本地socket获取IP(兼容无netifaces的场景)
|
|
135
109
|
"""
|
|
136
|
-
# 方案1:socket绑定内网地址(避免访问公网)
|
|
137
110
|
try:
|
|
111
|
+
# 创建socket但不连接任何地址,仅绑定到本地
|
|
138
112
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
139
|
-
|
|
113
|
+
# 绑定到0.0.0.0:0(仅用于获取本机IP,不发送数据)
|
|
114
|
+
s.bind(('', 0))
|
|
140
115
|
local_ip = s.getsockname()[0]
|
|
141
116
|
s.close()
|
|
117
|
+
# 过滤回环地址
|
|
142
118
|
if not local_ip.startswith('127.'):
|
|
143
119
|
return local_ip
|
|
120
|
+
return None
|
|
144
121
|
except Exception:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# 方案2:遍历所有本地IP(通过hostname解析)
|
|
148
|
-
try:
|
|
149
|
-
hostname = socket.gethostname()
|
|
150
|
-
ip_list = socket.gethostbyname_ex(hostname)[2]
|
|
151
|
-
for ip in ip_list:
|
|
152
|
-
if not ip.startswith('127.'):
|
|
153
|
-
return ip
|
|
154
|
-
except Exception:
|
|
155
|
-
pass
|
|
156
|
-
|
|
157
|
-
return None
|
|
122
|
+
return None
|
|
158
123
|
|
|
159
124
|
def _hash_to_machine_id(self, text: str) -> int:
|
|
160
125
|
"""将字符串哈希后取模,得到0~1023的machine_id(保证分布均匀)"""
|
|
@@ -163,60 +128,42 @@ class Snowflake:
|
|
|
163
128
|
return hash_int % self.MAX_MACHINE_ID
|
|
164
129
|
|
|
165
130
|
def _get_current_timestamp(self) -> int:
|
|
166
|
-
"""获取当前毫秒级时间戳"""
|
|
167
131
|
return int(time.time() * 1000)
|
|
168
132
|
|
|
169
133
|
def _wait_next_millisecond(self, current_timestamp: int) -> int:
|
|
170
|
-
"""等待直到下一个毫秒,避免序列耗尽"""
|
|
171
134
|
while current_timestamp <= self.last_timestamp:
|
|
172
135
|
current_timestamp = self._get_current_timestamp()
|
|
173
136
|
return current_timestamp
|
|
174
137
|
|
|
175
138
|
def generate_id(self) -> int:
|
|
176
|
-
|
|
177
|
-
|
|
139
|
+
with self.lock:
|
|
140
|
+
current_timestamp = self._get_current_timestamp()
|
|
178
141
|
|
|
179
|
-
|
|
180
|
-
time_diff = self.last_timestamp - current_timestamp
|
|
181
|
-
if time_diff > 0:
|
|
182
|
-
if time_diff > self.CLOCK_BACKWARD_THRESHOLD:
|
|
142
|
+
if current_timestamp < self.last_timestamp:
|
|
183
143
|
raise RuntimeError(
|
|
184
|
-
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp})
|
|
185
|
-
f"差值{time_diff}ms(阈值{self.CLOCK_BACKWARD_THRESHOLD}ms)"
|
|
144
|
+
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp})"
|
|
186
145
|
)
|
|
187
|
-
# 轻微回拨:等待时钟追上
|
|
188
|
-
current_timestamp = self._wait_next_millisecond(current_timestamp)
|
|
189
146
|
|
|
190
|
-
|
|
191
|
-
if current_timestamp != self.last_timestamp:
|
|
192
|
-
with self.lock:
|
|
193
|
-
self.last_timestamp = current_timestamp
|
|
194
|
-
self.sequence = 0
|
|
195
|
-
else:
|
|
196
|
-
with self.lock:
|
|
147
|
+
if current_timestamp == self.last_timestamp:
|
|
197
148
|
self.sequence = (self.sequence + 1) & self.MAX_SEQUENCE
|
|
198
149
|
if self.sequence == 0:
|
|
199
150
|
current_timestamp = self._wait_next_millisecond(
|
|
200
151
|
current_timestamp)
|
|
201
|
-
|
|
152
|
+
else:
|
|
153
|
+
self.sequence = 0
|
|
202
154
|
|
|
203
|
-
|
|
204
|
-
snowflake_id = (
|
|
205
|
-
((current_timestamp - self.START_TIMESTAMP) << self.TIMESTAMP_SHIFT)
|
|
206
|
-
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
207
|
-
| self.sequence
|
|
208
|
-
)
|
|
155
|
+
self.last_timestamp = current_timestamp
|
|
209
156
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
157
|
+
snowflake_id = (
|
|
158
|
+
((current_timestamp - self.START_TIMESTAMP) << self.TIMESTAMP_SHIFT)
|
|
159
|
+
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
160
|
+
| self.sequence
|
|
161
|
+
)
|
|
214
162
|
|
|
215
|
-
|
|
163
|
+
return snowflake_id
|
|
216
164
|
|
|
217
165
|
@staticmethod
|
|
218
166
|
def parse_id(snowflake_id: int) -> dict:
|
|
219
|
-
"""解析雪花ID,返回生成时间、机器ID、序列等信息"""
|
|
220
167
|
from datetime import datetime
|
|
221
168
|
sequence = snowflake_id & Snowflake.MAX_SEQUENCE
|
|
222
169
|
machine_id = (snowflake_id >>
|
|
@@ -230,71 +177,45 @@ class Snowflake:
|
|
|
230
177
|
"snowflake_id": snowflake_id,
|
|
231
178
|
"generate_time": generate_time,
|
|
232
179
|
"machine_id": machine_id,
|
|
233
|
-
"sequence": sequence
|
|
234
|
-
"is_java_long_safe": snowflake_id <= Snowflake._MAX_JAVA_LONG
|
|
180
|
+
"sequence": sequence
|
|
235
181
|
}
|
|
236
182
|
|
|
237
183
|
@classmethod
|
|
238
184
|
def next_id(cls) -> str:
|
|
239
185
|
"""
|
|
240
|
-
生成雪花ID
|
|
186
|
+
生成雪花ID(单例模式,避免重复创建实例)
|
|
241
187
|
:return: 雪花ID字符串
|
|
242
188
|
"""
|
|
189
|
+
# 单例模式创建实例
|
|
243
190
|
if cls._instance is None:
|
|
244
191
|
with cls._instance_lock:
|
|
245
192
|
if cls._instance is None:
|
|
246
|
-
# 锁内初始化,避免多线程重复计算machine_id
|
|
247
193
|
cls._instance = cls()
|
|
194
|
+
# 生成ID并转为字符串返回
|
|
248
195
|
return str(cls._instance.generate_id())
|
|
249
196
|
|
|
250
|
-
@ClassProperty
|
|
251
|
-
def id(cls) -> str:
|
|
252
|
-
"""
|
|
253
|
-
直接通过 `Snowflake.id` 属性生成雪花ID(兼容Python 3.11+)
|
|
254
|
-
:return: 雪花ID字符串
|
|
255
|
-
"""
|
|
256
|
-
return cls.next_id()
|
|
257
|
-
|
|
258
197
|
|
|
259
198
|
if __name__ == "__main__":
|
|
260
|
-
|
|
261
|
-
#
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
duplicate_count += 1
|
|
284
|
-
id_set.add(snow_id)
|
|
285
|
-
print(f"总生成数量: 10000")
|
|
286
|
-
print(f"唯一ID数量: {len(id_set)}")
|
|
287
|
-
print(f"重复ID数量: {duplicate_count}")
|
|
288
|
-
print(f"机器ID: {Snowflake._instance.machine_id}")
|
|
289
|
-
|
|
290
|
-
# 4. 高并发测试
|
|
291
|
-
import concurrent.futures
|
|
292
|
-
print("\n=== 高并发测试(100线程)===")
|
|
293
|
-
id_set_concurrent = set()
|
|
294
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
|
|
295
|
-
futures = [executor.submit(lambda: Snowflake.id) for _ in range(10000)]
|
|
296
|
-
for future in concurrent.futures.as_completed(futures):
|
|
297
|
-
id_set_concurrent.add(future.result())
|
|
298
|
-
print(f"高并发生成唯一ID数量: {len(id_set_concurrent)}")
|
|
299
|
-
|
|
300
|
-
print("\n=== 生产级雪花算法验证通过 ===")
|
|
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,25 +1,25 @@
|
|
|
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
|
|
7
7
|
Requires-Dist: aio-pika>=9.5.8
|
|
8
8
|
Requires-Dist: aiohttp>=3.13.2
|
|
9
|
-
Requires-Dist: aiomysql>=0.3.2
|
|
10
9
|
Requires-Dist: decorator>=5.2.1
|
|
11
|
-
Requires-Dist: fastapi>=0.
|
|
12
|
-
Requires-Dist: kafka-python>=2.
|
|
10
|
+
Requires-Dist: fastapi>=0.121.2
|
|
11
|
+
Requires-Dist: kafka-python>=2.2.16
|
|
13
12
|
Requires-Dist: loguru>=0.7.3
|
|
14
13
|
Requires-Dist: mysql-connector-python>=9.5.0
|
|
15
14
|
Requires-Dist: nacos-sdk-python<3.0,>=2.0.9
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist: pydantic>=2.12.
|
|
15
|
+
Requires-Dist: netifaces>=0.11.0
|
|
16
|
+
Requires-Dist: pydantic>=2.12.4
|
|
18
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
19
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
20
|
-
Requires-Dist: sqlalchemy
|
|
21
|
-
Requires-Dist: starlette>=0.
|
|
22
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.44
|
|
20
|
+
Requires-Dist: starlette>=0.49.3
|
|
21
|
+
Requires-Dist: uuid>=1.30
|
|
22
|
+
Requires-Dist: uvicorn>=0.38.0
|
|
23
23
|
|
|
24
24
|
# sycommon-python-lib
|
|
25
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
command/cli.py,sha256=bP2LCLkRvfETIwWkVD70q5xFxMI4D3BpH09Ws1f-ENc,5849
|
|
2
2
|
sycommon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
sycommon/services.py,sha256=
|
|
3
|
+
sycommon/services.py,sha256=66YUddNJsbx8axuxUZ6ERr95yphlhLOUbBUJ_tgNseI,11851
|
|
4
4
|
sycommon/config/Config.py,sha256=9yO5b8WfvEDvkyrGrlwrLFasgh_-MjcEvGF20Gz5Xo4,3041
|
|
5
5
|
sycommon/config/DatabaseConfig.py,sha256=ILiUuYT9_xJZE2W-RYuC3JCt_YLKc1sbH13-MHIOPhg,804
|
|
6
6
|
sycommon/config/EmbeddingConfig.py,sha256=gPKwiDYbeu1GpdIZXMmgqM7JqBIzCXi0yYuGRLZooMI,362
|
|
@@ -8,8 +8,6 @@ sycommon/config/LLMConfig.py,sha256=yU-aIqePIeF6msfRVEtGq7SXZVDfHyTi6JduKjhMO_4,
|
|
|
8
8
|
sycommon/config/MQConfig.py,sha256=_RDcmIdyWKjmgM5ZnriOoI-DpaxgXs7CD0awdAD6z88,252
|
|
9
9
|
sycommon/config/RerankerConfig.py,sha256=dohekaY_eTynmMimIuKHBYGXXQO6rJjSkm94OPLuMik,322
|
|
10
10
|
sycommon/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
sycommon/database/async_base_db_service.py,sha256=w6ONUiTtF4-bXRnkBt9QpL9BAy0XUDbQG7F9Hf2rfjw,1337
|
|
12
|
-
sycommon/database/async_database_service.py,sha256=4Ag5PH6DFEcJOXR8MRF9V_Jho5uCoU9Ibo3PqulDsXw,3916
|
|
13
11
|
sycommon/database/base_db_service.py,sha256=J5ELHMNeGfzA6zVcASPSPZ0XNKrRY3_gdGmVkZw3Mto,946
|
|
14
12
|
sycommon/database/database_service.py,sha256=mun5vgM7nkuH6_UyHLHqQ2Qk_5gRgMxJu4_obIKLT6o,3253
|
|
15
13
|
sycommon/health/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -17,13 +15,11 @@ sycommon/health/health_check.py,sha256=EhfbhspRpQiKJaxdtE-PzpKQO_ucaFKtQxIm16F5M
|
|
|
17
15
|
sycommon/health/metrics.py,sha256=fHqO73JuhoZkNPR-xIlxieXiTCvttq-kG-tvxag1s1s,268
|
|
18
16
|
sycommon/health/ping.py,sha256=FTlnIKk5y1mPfS1ZGOeT5IM_2udF5aqVLubEtuBp18M,250
|
|
19
17
|
sycommon/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
sycommon/logging/
|
|
21
|
-
sycommon/logging/kafka_log.py,sha256=sVw-dFZKEgCosjSUqgTj7YrpK-ggXhleZFwMUVhl-K0,21416
|
|
22
|
-
sycommon/logging/logger_levels.py,sha256=_-uQ_T1N8NkNgcAmLrMmJ83nHTDw5ZNvXFPvdk89XGY,1144
|
|
18
|
+
sycommon/logging/kafka_log.py,sha256=Ctjpch9clULhRcSKFVlAjXDmEZ63KVHqch4xzXAQZdQ,21174
|
|
23
19
|
sycommon/logging/logger_wrapper.py,sha256=TiHsrIIHiQMzXgXK12-0KIpU9GhwQJOoHslakzmq2zc,357
|
|
24
20
|
sycommon/logging/sql_logger.py,sha256=aEU3OGnI_51Tjyuuf4FpUi9KPTceFRuKAOyQbPzGhzM,2021
|
|
25
21
|
sycommon/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
sycommon/middleware/context.py,sha256=
|
|
22
|
+
sycommon/middleware/context.py,sha256=_5ghpv4u_yAvjkgM-XDx8OnO-YI1XtntHrXsHJHZkwo,88
|
|
27
23
|
sycommon/middleware/cors.py,sha256=0B5d_ovD56wcH9TfktRs88Q09R9f8Xx5h5ALWYvE8Iw,600
|
|
28
24
|
sycommon/middleware/docs.py,sha256=bVdDBIHXGVBv562MQLSroa1DgHoObxR9gFzv71PIejg,1187
|
|
29
25
|
sycommon/middleware/exception.py,sha256=Bk8IchNND1wg6tUX9hf4xWeEJhvA-E_zE9ysBpRZrqE,3010
|
|
@@ -31,7 +27,7 @@ sycommon/middleware/middleware.py,sha256=SzZ4wufSNdwC4Ppw99TE7a6AVGkrZRc55NHSrA3
|
|
|
31
27
|
sycommon/middleware/monitor_memory.py,sha256=pYRK-wRuDd6enSg9Pf8tQxPdYQS6S0AyjyXeKFRLKEs,628
|
|
32
28
|
sycommon/middleware/mq.py,sha256=4wBqiT5wJGcrfjk2GSr0_U3TStBxoNpHTzcRxVlMEHE,183
|
|
33
29
|
sycommon/middleware/timeout.py,sha256=fImlAPLm4Oa8N9goXtT_0os1GZPCi9F92OgXU81DgDU,656
|
|
34
|
-
sycommon/middleware/traceid.py,sha256=
|
|
30
|
+
sycommon/middleware/traceid.py,sha256=ugqPgHdUydj7m481rM_RH-yrIK9hAdkLemdPSSOnQvw,6821
|
|
35
31
|
sycommon/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
32
|
sycommon/models/base_http.py,sha256=EICAAibx3xhjBsLqm35Mi3DCqxp0FME4rD_3iQVjT_E,3051
|
|
37
33
|
sycommon/models/log.py,sha256=rZpj6VkDRxK3B6H7XSeWdYZshU8F0Sks8bq1p6pPlDw,500
|
|
@@ -48,17 +44,16 @@ sycommon/sse/sse.py,sha256=__CfWEcYxOxQ-HpLor4LTZ5hLWqw9-2X7CngqbVHsfw,10128
|
|
|
48
44
|
sycommon/synacos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
45
|
sycommon/synacos/example.py,sha256=61XL03tU8WTNOo3FUduf93F2fAwah1S0lbH1ufhRhRk,5739
|
|
50
46
|
sycommon/synacos/example2.py,sha256=adUaru3Hy482KrOA17DfaC4nwvLj8etIDS_KrWLWmCU,4811
|
|
51
|
-
sycommon/synacos/feign.py,sha256=
|
|
52
|
-
sycommon/synacos/feign_client.py,sha256=
|
|
53
|
-
sycommon/synacos/nacos_service.py,sha256=
|
|
47
|
+
sycommon/synacos/feign.py,sha256=U7RJFV8qPgWyFDpeIgmYpYnpXPrWj01kkigDf1k8sBY,8023
|
|
48
|
+
sycommon/synacos/feign_client.py,sha256=qEJNQFX5irDnD6VwN79im42h9FKzoELPmdEAXzgwPT0,15435
|
|
49
|
+
sycommon/synacos/nacos_service.py,sha256=tyh_JOjjoCGiKCr1xfU7MAmu7dDQCZmTzmYsSqNjiQY,35465
|
|
54
50
|
sycommon/synacos/param.py,sha256=KcfSkxnXOa0TGmCjY8hdzU9pzUsA8-4PeyBKWI2-568,1765
|
|
55
51
|
sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
52
|
sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
|
|
57
|
-
sycommon/tools/
|
|
58
|
-
sycommon/tools/snowflake.py,sha256=lVEe5mNCOgz5OqGQpf5_nXaGnRJlI2STX2s-ppTtanA,11947
|
|
53
|
+
sycommon/tools/snowflake.py,sha256=S3KWKFUKhgZ9QnzLvvaGTt2MavF9RvjDQqeUM99u6Kk,8589
|
|
59
54
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
60
|
-
sycommon_python_lib-0.1.
|
|
61
|
-
sycommon_python_lib-0.1.
|
|
62
|
-
sycommon_python_lib-0.1.
|
|
63
|
-
sycommon_python_lib-0.1.
|
|
64
|
-
sycommon_python_lib-0.1.
|
|
55
|
+
sycommon_python_lib-0.1.55a0.dist-info/METADATA,sha256=hlbD-d9IAS0lnH_ZdWhyA3c0ewXabuEFjcxeFetwBJw,7077
|
|
56
|
+
sycommon_python_lib-0.1.55a0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
sycommon_python_lib-0.1.55a0.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
58
|
+
sycommon_python_lib-0.1.55a0.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
59
|
+
sycommon_python_lib-0.1.55a0.dist-info/RECORD,,
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from contextlib import asynccontextmanager
|
|
2
|
-
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
|
3
|
-
from sycommon.config.Config import SingletonMeta
|
|
4
|
-
from sycommon.database.async_database_service import AsyncDatabaseService
|
|
5
|
-
from sycommon.logging.kafka_log import SYLogger
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class AsyncBaseDBService(metaclass=SingletonMeta):
|
|
9
|
-
"""数据库操作基础服务类,封装异步会话管理功能"""
|
|
10
|
-
|
|
11
|
-
def __init__(self):
|
|
12
|
-
# 获取异步引擎 (假设 DatabaseService.engine() 返回的是 AsyncEngine)
|
|
13
|
-
self.engine = AsyncDatabaseService.engine()
|
|
14
|
-
|
|
15
|
-
# 创建异步 Session 工厂
|
|
16
|
-
# class_=AsyncSession 是必须的,用于指定生成的是异步会话
|
|
17
|
-
self.Session = async_sessionmaker(
|
|
18
|
-
bind=self.engine,
|
|
19
|
-
class_=AsyncSession,
|
|
20
|
-
expire_on_commit=False
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
@asynccontextmanager
|
|
24
|
-
async def session(self):
|
|
25
|
-
"""
|
|
26
|
-
异步数据库会话上下文管理器
|
|
27
|
-
自动处理会话的创建、提交、回滚和关闭
|
|
28
|
-
"""
|
|
29
|
-
async with self.Session() as session:
|
|
30
|
-
try:
|
|
31
|
-
yield session
|
|
32
|
-
await session.commit()
|
|
33
|
-
except Exception as e:
|
|
34
|
-
await session.rollback()
|
|
35
|
-
SYLogger.error(f"Database operation failed: {str(e)}")
|
|
36
|
-
raise
|