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.
@@ -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
- header_key = meta.get_key(name).lower()
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
- if "application/x-www-form-urlencoded" in content_type_lower:
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": {k.lower(): v for k, v in headers.items()} if headers else {},
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):
@@ -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 psutil
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
- """雪花算法生成器(生产级优化版,无公网依赖,适配内网/K8s环境)"""
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(psutil读取) > 容器主机名 > 进程+时间+随机数(最终兜底)
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(替换netifaces,使用psutil)
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. 最终兜底:增加熵值(进程ID+毫秒时间戳+随机数),大幅降低重复概率
105
- fallback_text = f"{os.getpid()}_{int(time.time()*1000)}_{random.randint(0, 100000)}"
106
- return self._hash_to_machine_id(fallback_text)
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
- 使用psutil读取本机网卡信息,获取非回环的内网IP(跨平台兼容,过滤lo/lo0等回环网卡)
90
+ 读取本机网卡信息,获取非回环的内网IP(无公网依赖)
111
91
  :return: 内网IP字符串,失败返回None
112
92
  """
113
93
  try:
114
- # 遍历所有网卡接口
115
- net_if_addrs = psutil.net_if_addrs()
116
- for interface_name, addrs in net_if_addrs.items():
117
- # 过滤回环/虚拟网卡(兼容lo、lo0、lo1、Loopback、virtual等)
118
- if (interface_name.lower().startswith("lo")
119
- or interface_name.lower() in ["loopback", "virtual"]):
120
- continue
121
- # 遍历该网卡的所有地址,优先返回第一个非回环IPv4
122
- for addr in addrs:
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 Exception:
129
- # psutil调用失败,降级到纯内置方法
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
- 增强版降级方案:纯Python内置方法,多维度获取内网IP(无第三方依赖)
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.connect(("192.168.0.1", 80))
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
- pass
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
- """生成雪花ID(生产级优化:优化锁粒度,容忍轻微时钟回拨)"""
177
- current_timestamp = self._get_current_timestamp()
142
+ with self.lock:
143
+ current_timestamp = self._get_current_timestamp()
178
144
 
179
- # 1. 处理时钟回拨:容忍CLOCK_BACKWARD_THRESHOLD内的微调,超过则抛异常
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
- # 2. 优化锁粒度:仅在同一毫秒内递增序列时加锁
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
- self.last_timestamp = current_timestamp
155
+ else:
156
+ self.sequence = 0
202
157
 
203
- # 3. 计算最终雪花ID
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
- # 最终校验:确保不超过Java Long最大值
211
- if snowflake_id > self._MAX_JAVA_LONG:
212
- raise RuntimeError(
213
- f"生成的雪花ID({snowflake_id})超过Java Long最大值({self._MAX_JAVA_LONG})")
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
- return snowflake_id
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("=== 生产级雪花算法ID生成测试 ===")
261
- # 1. 基础生成测试
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
- # 2. 解析ID信息
271
- print("\n=== 雪花ID解析 ===")
272
- parse_info = Snowflake.parse_id(int(id3))
273
- for key, value in parse_info.items():
274
- print(f"{key}: {value}")
220
+ # 原有方式仍可正常使用
221
+ print("\n=== 原有方法方式生成 ===")
222
+ id4 = Snowflake.next_id()
223
+ print(f"生成ID4: {id4}")
275
224
 
276
- # 3. 批量唯一性验证(10000个ID)
277
- print("\n=== 批量唯一性验证(10000个)===")
225
+ # 批量验证(1000个唯一ID)
278
226
  id_set = set()
279
- duplicate_count = 0
280
- for i in range(10000):
281
- snow_id = Snowflake.id
282
- if snow_id in id_set:
283
- duplicate_count += 1
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
- 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=== 生产级雪花算法验证通过 ===")
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.55
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: psutil>=7.1.3
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[asyncio]>=2.0.45
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=pioA4E2RrV6nyAY8EuETUt4mKzCYoxD1rTMYCcppqjE,11581
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/async_sql_logger.py,sha256=_OY36XkUm__U3NhMgiecy-qd-nptZ_0gpE3J8lGAr58,2619
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=sc1UjN55dYww2tT9KdFMJV8mgpGro67SnETzbVzty-s,155
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=0WYLV5PR6sGeqhLdviHYW94UhUC7NJua2g5HWf2Zhbc,13447
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=frB3D5LeFDtT3pJLFOwFzEOrNAJKeQNGk-BzUg9T3WM,8295
52
- sycommon/synacos/feign_client.py,sha256=ExO7Pd5B3eFKDjXqBRc260K1jkI49IYguLwJJaD2R-o,16166
53
- sycommon/synacos/nacos_service.py,sha256=jeg7Bd-SYb0nB1vpjN4sJdw_0nXuI8GjebdM7zu_Z8c,35404
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/merge_headers.py,sha256=HV_i52Q-9se3SP8qh7ZGYl8bP7Fxtal4CGVkyMwEdM8,4373
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.55.dist-info/METADATA,sha256=P0jS9Alm326fol0Jzj_u7HcpJtsugkCpMyCv9z0OLzc,7084
61
- sycommon_python_lib-0.1.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
- sycommon_python_lib-0.1.55.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
63
- sycommon_python_lib-0.1.55.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
64
- sycommon_python_lib-0.1.55.dist-info/RECORD,,
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