sycommon-python-lib 0.1.55__py3-none-any.whl → 0.1.55b0__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 +1 -13
- sycommon/middleware/context.py +0 -2
- sycommon/middleware/traceid.py +29 -150
- sycommon/services.py +51 -54
- sycommon/synacos/feign.py +3 -8
- sycommon/synacos/feign_client.py +8 -22
- sycommon/synacos/nacos_service.py +2 -0
- sycommon/tools/snowflake.py +60 -125
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55b0.dist-info}/METADATA +3 -4
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55b0.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.55b0.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55b0.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.55.dist-info → sycommon_python_lib-0.1.55b0.dist-info}/top_level.txt +0 -0
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,8 +28,6 @@ def feign_client(
|
|
|
29
28
|
default_headers: Optional[Dict[str, str]] = None
|
|
30
29
|
):
|
|
31
30
|
default_headers = default_headers or {}
|
|
32
|
-
default_headers = {k.lower(): v for k, v in default_headers.items()}
|
|
33
|
-
default_headers = merge_headers(SYLogger.get_headers(), default_headers)
|
|
34
31
|
default_headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.id
|
|
35
32
|
|
|
36
33
|
def decorator(cls):
|
|
@@ -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 = merge_headers(SYLogger.get_headers(), headers)
|
|
164
158
|
headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.id
|
|
165
|
-
|
|
166
|
-
# 处理参数中的Header类型
|
|
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,10 +3,9 @@ import threading
|
|
|
3
3
|
import socket
|
|
4
4
|
import hashlib
|
|
5
5
|
import random
|
|
6
|
-
import os
|
|
7
6
|
from typing import Optional, Type, Any
|
|
8
7
|
from os import environ
|
|
9
|
-
import
|
|
8
|
+
import netifaces
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class ClassProperty:
|
|
@@ -24,8 +23,7 @@ class ClassProperty:
|
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
class Snowflake:
|
|
27
|
-
"""
|
|
28
|
-
# 基础配置(可根据业务调整)
|
|
26
|
+
"""雪花算法生成器(无公网依赖,适配内网环境)"""
|
|
29
27
|
START_TIMESTAMP = 1388534400000 # 2014-01-01 00:00:00
|
|
30
28
|
SEQUENCE_BITS = 12
|
|
31
29
|
MACHINE_ID_BITS = 10
|
|
@@ -33,10 +31,8 @@ class Snowflake:
|
|
|
33
31
|
MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1
|
|
34
32
|
MACHINE_ID_SHIFT = SEQUENCE_BITS
|
|
35
33
|
TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS
|
|
36
|
-
CLOCK_BACKWARD_THRESHOLD = 5 # 容忍的时钟回拨阈值(毫秒)
|
|
37
|
-
_MAX_JAVA_LONG = 9223372036854775807 # Java Long最大值
|
|
38
34
|
|
|
39
|
-
#
|
|
35
|
+
# 类级别的单例实例
|
|
40
36
|
_instance = None
|
|
41
37
|
_instance_lock = threading.Lock()
|
|
42
38
|
|
|
@@ -45,38 +41,22 @@ class Snowflake:
|
|
|
45
41
|
初始化:优先使用传入的machine_id,否则自动从K8s环境获取
|
|
46
42
|
:param machine_id: 手动指定机器ID(None则自动计算)
|
|
47
43
|
"""
|
|
48
|
-
# 前置校验:确保雪花ID不会超过Java Long最大值
|
|
49
|
-
self._validate_timestamp_range()
|
|
50
|
-
|
|
51
44
|
# 自动计算K8s环境下的machine_id
|
|
52
45
|
if machine_id is None:
|
|
53
46
|
machine_id = self._get_k8s_machine_id()
|
|
54
47
|
|
|
55
|
-
# 校验machine_id合法性
|
|
56
48
|
if not (0 <= machine_id <= self.MAX_MACHINE_ID):
|
|
57
49
|
raise ValueError(f"机器ID必须在0~{self.MAX_MACHINE_ID}之间")
|
|
58
50
|
|
|
59
|
-
# 初始化核心参数
|
|
60
51
|
self.machine_id = machine_id
|
|
61
52
|
self.last_timestamp = -1
|
|
62
53
|
self.sequence = 0
|
|
63
54
|
self.lock = threading.Lock()
|
|
64
55
|
|
|
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
56
|
def _get_k8s_machine_id(self) -> int:
|
|
77
57
|
"""
|
|
78
|
-
从K8s环境自动计算唯一machine_id
|
|
79
|
-
优先级:POD_NAME > POD_IP > 容器内网IP
|
|
58
|
+
从K8s环境自动计算唯一machine_id(无公网依赖,多层兜底):
|
|
59
|
+
优先级:POD_NAME > POD_IP > 容器内网IP(网卡读取) > 容器主机名 > 随机数(最终兜底)
|
|
80
60
|
"""
|
|
81
61
|
# 1. 优先读取K8s内置的POD_NAME(默认注入,优先级最高)
|
|
82
62
|
pod_name = environ.get("POD_NAME")
|
|
@@ -88,7 +68,7 @@ class Snowflake:
|
|
|
88
68
|
if pod_ip:
|
|
89
69
|
return self._hash_to_machine_id(pod_ip)
|
|
90
70
|
|
|
91
|
-
# 3. 兜底1:读取本机网卡获取内网IP
|
|
71
|
+
# 3. 兜底1:读取本机网卡获取内网IP(无公网依赖)
|
|
92
72
|
try:
|
|
93
73
|
local_ip = self._get_local_internal_ip()
|
|
94
74
|
if local_ip:
|
|
@@ -101,60 +81,48 @@ class Snowflake:
|
|
|
101
81
|
if hostname:
|
|
102
82
|
return self._hash_to_machine_id(hostname)
|
|
103
83
|
|
|
104
|
-
# 5.
|
|
105
|
-
|
|
106
|
-
return
|
|
84
|
+
# 5. 最终兜底:生成随机数(仅极端情况使用)
|
|
85
|
+
random_id = random.randint(0, self.MAX_MACHINE_ID)
|
|
86
|
+
return random_id
|
|
107
87
|
|
|
108
88
|
def _get_local_internal_ip(self) -> Optional[str]:
|
|
109
89
|
"""
|
|
110
|
-
|
|
90
|
+
读取本机网卡信息,获取非回环的内网IP(无公网依赖)
|
|
111
91
|
:return: 内网IP字符串,失败返回None
|
|
112
92
|
"""
|
|
113
93
|
try:
|
|
114
|
-
#
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if addr.family == psutil.AF_INET:
|
|
124
|
-
ip = addr.address
|
|
94
|
+
# 遍历所有网卡
|
|
95
|
+
for interface in netifaces.interfaces():
|
|
96
|
+
# 获取网卡的IP地址信息
|
|
97
|
+
addrs = netifaces.ifaddresses(interface)
|
|
98
|
+
# 只取IPv4地址
|
|
99
|
+
if netifaces.AF_INET in addrs:
|
|
100
|
+
for addr in addrs[netifaces.AF_INET]:
|
|
101
|
+
ip = addr.get('addr')
|
|
102
|
+
# 过滤回环地址(127.0.0.1)
|
|
125
103
|
if ip and not ip.startswith('127.'):
|
|
126
104
|
return ip
|
|
127
105
|
return None
|
|
128
|
-
except
|
|
129
|
-
#
|
|
106
|
+
except ImportError:
|
|
107
|
+
# 若未安装netifaces,降级为socket方式
|
|
130
108
|
return self._get_local_ip_fallback()
|
|
131
109
|
|
|
132
110
|
def _get_local_ip_fallback(self) -> Optional[str]:
|
|
133
111
|
"""
|
|
134
|
-
|
|
112
|
+
降级方案:不连接公网,仅通过本地socket获取IP(兼容无netifaces的场景)
|
|
135
113
|
"""
|
|
136
|
-
# 方案1:socket绑定内网地址(避免访问公网)
|
|
137
114
|
try:
|
|
115
|
+
# 创建socket但不连接任何地址,仅绑定到本地
|
|
138
116
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
139
|
-
s.
|
|
117
|
+
s.bind(('', 0))
|
|
140
118
|
local_ip = s.getsockname()[0]
|
|
141
119
|
s.close()
|
|
120
|
+
# 过滤回环地址
|
|
142
121
|
if not local_ip.startswith('127.'):
|
|
143
122
|
return local_ip
|
|
123
|
+
return None
|
|
144
124
|
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
|
|
125
|
+
return None
|
|
158
126
|
|
|
159
127
|
def _hash_to_machine_id(self, text: str) -> int:
|
|
160
128
|
"""将字符串哈希后取模,得到0~1023的machine_id(保证分布均匀)"""
|
|
@@ -163,60 +131,42 @@ class Snowflake:
|
|
|
163
131
|
return hash_int % self.MAX_MACHINE_ID
|
|
164
132
|
|
|
165
133
|
def _get_current_timestamp(self) -> int:
|
|
166
|
-
"""获取当前毫秒级时间戳"""
|
|
167
134
|
return int(time.time() * 1000)
|
|
168
135
|
|
|
169
136
|
def _wait_next_millisecond(self, current_timestamp: int) -> int:
|
|
170
|
-
"""等待直到下一个毫秒,避免序列耗尽"""
|
|
171
137
|
while current_timestamp <= self.last_timestamp:
|
|
172
138
|
current_timestamp = self._get_current_timestamp()
|
|
173
139
|
return current_timestamp
|
|
174
140
|
|
|
175
141
|
def generate_id(self) -> int:
|
|
176
|
-
|
|
177
|
-
|
|
142
|
+
with self.lock:
|
|
143
|
+
current_timestamp = self._get_current_timestamp()
|
|
178
144
|
|
|
179
|
-
|
|
180
|
-
time_diff = self.last_timestamp - current_timestamp
|
|
181
|
-
if time_diff > 0:
|
|
182
|
-
if time_diff > self.CLOCK_BACKWARD_THRESHOLD:
|
|
145
|
+
if current_timestamp < self.last_timestamp:
|
|
183
146
|
raise RuntimeError(
|
|
184
|
-
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp})
|
|
185
|
-
f"差值{time_diff}ms(阈值{self.CLOCK_BACKWARD_THRESHOLD}ms)"
|
|
147
|
+
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp})"
|
|
186
148
|
)
|
|
187
|
-
# 轻微回拨:等待时钟追上
|
|
188
|
-
current_timestamp = self._wait_next_millisecond(current_timestamp)
|
|
189
149
|
|
|
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:
|
|
150
|
+
if current_timestamp == self.last_timestamp:
|
|
197
151
|
self.sequence = (self.sequence + 1) & self.MAX_SEQUENCE
|
|
198
152
|
if self.sequence == 0:
|
|
199
153
|
current_timestamp = self._wait_next_millisecond(
|
|
200
154
|
current_timestamp)
|
|
201
|
-
|
|
155
|
+
else:
|
|
156
|
+
self.sequence = 0
|
|
202
157
|
|
|
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
|
-
)
|
|
158
|
+
self.last_timestamp = current_timestamp
|
|
209
159
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
160
|
+
snowflake_id = (
|
|
161
|
+
((current_timestamp - self.START_TIMESTAMP) << self.TIMESTAMP_SHIFT)
|
|
162
|
+
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
163
|
+
| self.sequence
|
|
164
|
+
)
|
|
214
165
|
|
|
215
|
-
|
|
166
|
+
return snowflake_id
|
|
216
167
|
|
|
217
168
|
@staticmethod
|
|
218
169
|
def parse_id(snowflake_id: int) -> dict:
|
|
219
|
-
"""解析雪花ID,返回生成时间、机器ID、序列等信息"""
|
|
220
170
|
from datetime import datetime
|
|
221
171
|
sequence = snowflake_id & Snowflake.MAX_SEQUENCE
|
|
222
172
|
machine_id = (snowflake_id >>
|
|
@@ -230,21 +180,21 @@ class Snowflake:
|
|
|
230
180
|
"snowflake_id": snowflake_id,
|
|
231
181
|
"generate_time": generate_time,
|
|
232
182
|
"machine_id": machine_id,
|
|
233
|
-
"sequence": sequence
|
|
234
|
-
"is_java_long_safe": snowflake_id <= Snowflake._MAX_JAVA_LONG
|
|
183
|
+
"sequence": sequence
|
|
235
184
|
}
|
|
236
185
|
|
|
237
186
|
@classmethod
|
|
238
187
|
def next_id(cls) -> str:
|
|
239
188
|
"""
|
|
240
|
-
生成雪花ID
|
|
189
|
+
生成雪花ID(单例模式,避免重复创建实例)
|
|
241
190
|
:return: 雪花ID字符串
|
|
242
191
|
"""
|
|
192
|
+
# 单例模式创建实例
|
|
243
193
|
if cls._instance is None:
|
|
244
194
|
with cls._instance_lock:
|
|
245
195
|
if cls._instance is None:
|
|
246
|
-
# 锁内初始化,避免多线程重复计算machine_id
|
|
247
196
|
cls._instance = cls()
|
|
197
|
+
# 生成ID并转为字符串返回
|
|
248
198
|
return str(cls._instance.generate_id())
|
|
249
199
|
|
|
250
200
|
@ClassProperty
|
|
@@ -257,8 +207,8 @@ class Snowflake:
|
|
|
257
207
|
|
|
258
208
|
|
|
259
209
|
if __name__ == "__main__":
|
|
260
|
-
print("===
|
|
261
|
-
#
|
|
210
|
+
print("=== 兼容Python 3.11+的属性方式生成雪花ID ===")
|
|
211
|
+
# 直接访问 Snowflake.id 即可生成,无废弃警告
|
|
262
212
|
id1 = Snowflake.id
|
|
263
213
|
id2 = Snowflake.id
|
|
264
214
|
id3 = Snowflake.id
|
|
@@ -267,34 +217,19 @@ if __name__ == "__main__":
|
|
|
267
217
|
print(f"生成ID3: {id3}")
|
|
268
218
|
print(f"ID是否唯一: {len({id1, id2, id3}) == 3}")
|
|
269
219
|
|
|
270
|
-
#
|
|
271
|
-
print("\n===
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
print(f"{key}: {value}")
|
|
220
|
+
# 原有方式仍可正常使用
|
|
221
|
+
print("\n=== 原有方法方式生成 ===")
|
|
222
|
+
id4 = Snowflake.next_id()
|
|
223
|
+
print(f"生成ID4: {id4}")
|
|
275
224
|
|
|
276
|
-
#
|
|
277
|
-
print("\n=== 批量唯一性验证(10000个)===")
|
|
225
|
+
# 批量验证(1000个唯一ID)
|
|
278
226
|
id_set = set()
|
|
279
|
-
|
|
280
|
-
for i in range(
|
|
281
|
-
snow_id = Snowflake.id
|
|
282
|
-
|
|
283
|
-
|
|
227
|
+
_MAX_JAVA_LONG = 9223372036854775807
|
|
228
|
+
for i in range(1000):
|
|
229
|
+
snow_id = Snowflake.id # 全程使用兼容版属性方式
|
|
230
|
+
id_num = int(snow_id)
|
|
231
|
+
assert id_num <= _MAX_JAVA_LONG, f"ID超过Java long最大值: {id_num}"
|
|
232
|
+
assert snow_id not in id_set, f"重复生成ID: {snow_id}"
|
|
284
233
|
id_set.add(snow_id)
|
|
285
|
-
|
|
286
|
-
print(f"
|
|
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=== 生产级雪花算法验证通过 ===")
|
|
234
|
+
|
|
235
|
+
print(f"\n成功生成{len(id_set)}个唯一雪花ID,兼容版属性访问方式验证通过!")
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.55b0
|
|
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
10
|
Requires-Dist: fastapi>=0.127.0
|
|
12
11
|
Requires-Dist: kafka-python>=2.3.0
|
|
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:
|
|
15
|
+
Requires-Dist: netifaces>=0.11.0
|
|
17
16
|
Requires-Dist: pydantic>=2.12.5
|
|
18
17
|
Requires-Dist: python-dotenv>=1.2.1
|
|
19
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
20
|
-
Requires-Dist: sqlalchemy
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.45
|
|
21
20
|
Requires-Dist: starlette>=0.50.0
|
|
22
21
|
Requires-Dist: uvicorn>=0.40.0
|
|
23
22
|
|
|
@@ -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=viqJ2hDqnyX5eUKkhIhU__kytIwe6nLuHIAFGcaRpUI,21118
|
|
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=FLZTxVAIboZvqK_S69eReeIzZGUDIVy6KTA1kZBoRyI,6908
|
|
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=xvyH_1no6gsggO3YYB0_88NWNA26odbQ_G-2MjApif0,8016
|
|
48
|
+
sycommon/synacos/feign_client.py,sha256=PYjTrnqMc_Jl6Wnpiz8-PFozCjPk6VGnPWv29JefL14,15421
|
|
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=wSATJzWGb6HcaZ1u_fdzF6I5seRviQQm2KS3Jf45nm4,8520
|
|
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.55b0.dist-info/METADATA,sha256=9K3VD62twbBtlxjoty9K0h96O74lp2NFfqtviimdqGM,7050
|
|
56
|
+
sycommon_python_lib-0.1.55b0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
sycommon_python_lib-0.1.55b0.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
58
|
+
sycommon_python_lib-0.1.55b0.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
59
|
+
sycommon_python_lib-0.1.55b0.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
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
|
2
|
-
from sqlalchemy import text
|
|
3
|
-
|
|
4
|
-
from sycommon.config.Config import SingletonMeta
|
|
5
|
-
from sycommon.config.DatabaseConfig import DatabaseConfig, convert_dict_keys
|
|
6
|
-
from sycommon.logging.kafka_log import SYLogger
|
|
7
|
-
from sycommon.logging.async_sql_logger import AsyncSQLTraceLogger
|
|
8
|
-
from sycommon.synacos.nacos_service import NacosService
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class AsyncDatabaseService(metaclass=SingletonMeta):
|
|
12
|
-
_engine = None
|
|
13
|
-
|
|
14
|
-
@staticmethod
|
|
15
|
-
async def setup_database(config: dict, shareConfigKey: str):
|
|
16
|
-
common = NacosService(config).share_configs.get(shareConfigKey, {})
|
|
17
|
-
if common and common.get('spring', {}).get('datasource', None):
|
|
18
|
-
databaseConfig = common.get('spring', {}).get('datasource', None)
|
|
19
|
-
converted_dict = convert_dict_keys(databaseConfig)
|
|
20
|
-
db_config = DatabaseConfig.model_validate(converted_dict)
|
|
21
|
-
|
|
22
|
-
# 初始化 DatabaseConnector (传入配置)
|
|
23
|
-
connector = AsyncDatabaseConnector(db_config)
|
|
24
|
-
|
|
25
|
-
# 赋值 engine
|
|
26
|
-
AsyncDatabaseService._engine = connector.engine
|
|
27
|
-
|
|
28
|
-
# 执行异步测试连接
|
|
29
|
-
if not await connector.test_connection():
|
|
30
|
-
raise Exception("Database connection test failed")
|
|
31
|
-
|
|
32
|
-
@staticmethod
|
|
33
|
-
def engine():
|
|
34
|
-
return AsyncDatabaseService._engine
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class AsyncDatabaseConnector(metaclass=SingletonMeta):
|
|
38
|
-
def __init__(self, db_config: DatabaseConfig):
|
|
39
|
-
# 从 DatabaseConfig 中提取数据库连接信息
|
|
40
|
-
self.db_user = db_config.username
|
|
41
|
-
self.db_password = db_config.password
|
|
42
|
-
|
|
43
|
-
# 提取 URL 中的主机、端口和数据库名
|
|
44
|
-
url_parts = db_config.url.split('//')[1].split('/')
|
|
45
|
-
host_port = url_parts[0].split(':')
|
|
46
|
-
self.db_host = host_port[0]
|
|
47
|
-
self.db_port = host_port[1]
|
|
48
|
-
self.db_name = url_parts[1].split('?')[0]
|
|
49
|
-
|
|
50
|
-
# 提取 URL 中的参数
|
|
51
|
-
params_str = url_parts[1].split('?')[1] if len(
|
|
52
|
-
url_parts[1].split('?')) > 1 else ''
|
|
53
|
-
params = {}
|
|
54
|
-
for param in params_str.split('&'):
|
|
55
|
-
if param:
|
|
56
|
-
key, value = param.split('=')
|
|
57
|
-
params[key] = value
|
|
58
|
-
|
|
59
|
-
# 在params中去掉指定的参数
|
|
60
|
-
for key in ['useUnicode', 'characterEncoding', 'serverTimezone', 'zeroDateTimeBehavior']:
|
|
61
|
-
if key in params:
|
|
62
|
-
del params[key]
|
|
63
|
-
|
|
64
|
-
# 构建数据库连接 URL
|
|
65
|
-
# 注意:这里将 mysqlconnector 替换为 aiomysql 以支持异步
|
|
66
|
-
self.db_url = f'mysql+aiomysql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}'
|
|
67
|
-
|
|
68
|
-
SYLogger.info(f"Database URL: {self.db_url}")
|
|
69
|
-
|
|
70
|
-
# 优化连接池配置
|
|
71
|
-
# 使用 create_async_engine 替代 create_engine
|
|
72
|
-
self.engine = create_async_engine(
|
|
73
|
-
self.db_url,
|
|
74
|
-
connect_args=params,
|
|
75
|
-
pool_size=10, # 连接池大小
|
|
76
|
-
max_overflow=20, # 最大溢出连接数
|
|
77
|
-
pool_timeout=30, # 连接超时时间(秒)
|
|
78
|
-
pool_recycle=3600, # 连接回收时间(秒)
|
|
79
|
-
pool_pre_ping=True, # 每次获取连接前检查连接是否有效
|
|
80
|
-
echo=False, # 打印 SQL 语句
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# 注册 SQL 日志拦截器 (注意:SQLTraceLogger 需要支持异步引擎,或者您可能需要调整日志逻辑)
|
|
84
|
-
# 假设 SQLTraceLogger.setup_sql_logging 能够处理 AsyncEngine
|
|
85
|
-
AsyncSQLTraceLogger.setup_sql_logging(self.engine)
|
|
86
|
-
|
|
87
|
-
async def test_connection(self):
|
|
88
|
-
try:
|
|
89
|
-
# 异步上下文管理器
|
|
90
|
-
async with self.engine.connect() as connection:
|
|
91
|
-
# 执行简单查询
|
|
92
|
-
await connection.execute(text("SELECT 1"))
|
|
93
|
-
return True
|
|
94
|
-
except Exception as e:
|
|
95
|
-
SYLogger.error(f"Database connection test failed: {e}")
|
|
96
|
-
return False
|