mdbq 4.0.115__py3-none-any.whl → 4.0.117__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mdbq might be problematic. Click here for more details.
- mdbq/__version__.py +1 -1
- mdbq/redis/redis_cache.py +119 -38
- mdbq/route/monitor.py +18 -5
- {mdbq-4.0.115.dist-info → mdbq-4.0.117.dist-info}/METADATA +2 -2
- {mdbq-4.0.115.dist-info → mdbq-4.0.117.dist-info}/RECORD +7 -7
- {mdbq-4.0.115.dist-info → mdbq-4.0.117.dist-info}/WHEEL +1 -1
- {mdbq-4.0.115.dist-info → mdbq-4.0.117.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '4.0.
|
|
1
|
+
VERSION = '4.0.117'
|
mdbq/redis/redis_cache.py
CHANGED
|
@@ -44,7 +44,7 @@ logger = mylogger.MyLogger(
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class CacheStatsCollector:
|
|
47
|
-
"""缓存统计收集器
|
|
47
|
+
"""缓存统计收集器 """
|
|
48
48
|
|
|
49
49
|
def __init__(self, enabled: bool = True, mysql_pool=None, config: dict = None):
|
|
50
50
|
self.enabled = enabled
|
|
@@ -67,10 +67,20 @@ class CacheStatsCollector:
|
|
|
67
67
|
self.response_times = deque(maxlen=1000)
|
|
68
68
|
self.namespace_stats = defaultdict(int)
|
|
69
69
|
|
|
70
|
-
#
|
|
70
|
+
# 定时提交控制
|
|
71
71
|
self.submit_interval = self.config.get('submit_interval', 600) # 每隔N秒定时提交一次(期间有新操作时)
|
|
72
72
|
self.last_submit_time = time.time()
|
|
73
73
|
self.last_operation_count = 0 # 上次提交时的操作总数
|
|
74
|
+
|
|
75
|
+
# 后台定时器
|
|
76
|
+
self._timer = None
|
|
77
|
+
self._shutdown_event = threading.Event()
|
|
78
|
+
self._error_count = 0 # 连续错误计数
|
|
79
|
+
self._max_errors = 5 # 最大连续错误次数
|
|
80
|
+
|
|
81
|
+
# 启动后台定时提交
|
|
82
|
+
if self.enabled and self.mysql_pool:
|
|
83
|
+
self._start_background_timer()
|
|
74
84
|
|
|
75
85
|
def record_operation(self, operation: str, response_time: float = 0, namespace: str = ""):
|
|
76
86
|
"""记录操作统计"""
|
|
@@ -101,15 +111,6 @@ class CacheStatsCollector:
|
|
|
101
111
|
'operation': operation,
|
|
102
112
|
'submit_error': str(submit_error)
|
|
103
113
|
})
|
|
104
|
-
|
|
105
|
-
logger.debug("缓存操作统计已记录", {
|
|
106
|
-
'operation': operation,
|
|
107
|
-
'response_time_ms': round(response_time, 2),
|
|
108
|
-
'namespace': namespace,
|
|
109
|
-
'process_id': self.process_id,
|
|
110
|
-
'old_total_operations': old_total,
|
|
111
|
-
'new_total_operations': self.stats['total_operations']
|
|
112
|
-
})
|
|
113
114
|
except Exception as e:
|
|
114
115
|
# 统计记录失败不应影响缓存操作
|
|
115
116
|
logger.error("统计记录失败,但缓存操作继续", {
|
|
@@ -119,8 +120,64 @@ class CacheStatsCollector:
|
|
|
119
120
|
'error': str(e)
|
|
120
121
|
})
|
|
121
122
|
|
|
122
|
-
def
|
|
123
|
-
"""
|
|
123
|
+
def _start_background_timer(self):
|
|
124
|
+
"""启动后台定时提交线程"""
|
|
125
|
+
if self._timer is not None:
|
|
126
|
+
return # 已经启动
|
|
127
|
+
|
|
128
|
+
logger.debug("启动后台定时提交", {
|
|
129
|
+
'instance_name': self.instance_name,
|
|
130
|
+
'submit_interval': self.submit_interval
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
self._timer = threading.Timer(self.submit_interval, self._background_submit)
|
|
134
|
+
self._timer.daemon = True # 设置为守护线程
|
|
135
|
+
self._timer.start()
|
|
136
|
+
|
|
137
|
+
def _background_submit(self):
|
|
138
|
+
"""后台定时提交方法"""
|
|
139
|
+
if self._shutdown_event.is_set():
|
|
140
|
+
return # 已关闭,不再提交
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
# 执行提交检查(强制检查,不受时间间隔限制)
|
|
144
|
+
self._check_and_submit(force_check=True)
|
|
145
|
+
# 成功执行,重置错误计数
|
|
146
|
+
self._error_count = 0
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
self._error_count += 1
|
|
150
|
+
logger.error("后台定时提交失败", {
|
|
151
|
+
'instance_name': self.instance_name,
|
|
152
|
+
'process_id': self.process_id,
|
|
153
|
+
'error': str(e),
|
|
154
|
+
'error_type': type(e).__name__,
|
|
155
|
+
'error_count': self._error_count,
|
|
156
|
+
'max_errors': self._max_errors
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
# 如果连续错误次数过多,停止定时器
|
|
160
|
+
if self._error_count >= self._max_errors:
|
|
161
|
+
logger.error("后台定时器连续错误过多,停止定时提交", {
|
|
162
|
+
'instance_name': self.instance_name,
|
|
163
|
+
'process_id': self.process_id,
|
|
164
|
+
'error_count': self._error_count
|
|
165
|
+
})
|
|
166
|
+
return # 不再安排下一次定时器
|
|
167
|
+
|
|
168
|
+
finally:
|
|
169
|
+
# 安排下一次定时提交(仅在未达到最大错误次数时)
|
|
170
|
+
if not self._shutdown_event.is_set() and self._error_count < self._max_errors:
|
|
171
|
+
self._timer = threading.Timer(self.submit_interval, self._background_submit)
|
|
172
|
+
self._timer.daemon = True
|
|
173
|
+
self._timer.start()
|
|
174
|
+
|
|
175
|
+
def _check_and_submit(self, force_check=False):
|
|
176
|
+
"""检查并提交统计数据
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
force_check: 是否强制检查(用于后台定时器)
|
|
180
|
+
"""
|
|
124
181
|
if not self.mysql_pool:
|
|
125
182
|
return
|
|
126
183
|
|
|
@@ -128,7 +185,9 @@ class CacheStatsCollector:
|
|
|
128
185
|
time_since_last_submit = current_time - self.last_submit_time
|
|
129
186
|
|
|
130
187
|
# 提交逻辑:每隔固定秒数且期间有新操作则提交
|
|
131
|
-
|
|
188
|
+
should_check_time = force_check or time_since_last_submit >= self.submit_interval
|
|
189
|
+
|
|
190
|
+
if should_check_time:
|
|
132
191
|
# 检查是否有新的操作(与上次提交时相比)
|
|
133
192
|
new_operations = self.stats['total_operations'] - self.last_operation_count
|
|
134
193
|
|
|
@@ -139,29 +198,26 @@ class CacheStatsCollector:
|
|
|
139
198
|
self.last_submit_time = current_time
|
|
140
199
|
self.last_operation_count = self.stats['total_operations']
|
|
141
200
|
|
|
142
|
-
logger.info("
|
|
201
|
+
logger.info("统计数据提交成功", {
|
|
143
202
|
'instance_name': self.instance_name,
|
|
144
|
-
'process_id': self.process_id,
|
|
145
203
|
'total_operations': self.stats['total_operations'],
|
|
146
204
|
'new_operations': new_operations,
|
|
147
|
-
'
|
|
148
|
-
'submit_interval': self.submit_interval
|
|
205
|
+
'trigger_type': 'background_timer' if force_check else 'operation_triggered'
|
|
149
206
|
})
|
|
150
207
|
except Exception as e:
|
|
151
208
|
logger.error("统计数据提交失败", {
|
|
152
209
|
'instance_name': self.instance_name,
|
|
153
|
-
'
|
|
154
|
-
'
|
|
210
|
+
'error': str(e),
|
|
211
|
+
'trigger_type': 'background_timer' if force_check else 'operation_triggered'
|
|
155
212
|
})
|
|
156
213
|
else:
|
|
157
214
|
# 无新操作,跳过提交但更新时间
|
|
158
215
|
self.last_submit_time = current_time
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
})
|
|
216
|
+
if force_check: # 仅在后台定时器触发时记录
|
|
217
|
+
logger.debug("后台检查:无新操作,跳过提交", {
|
|
218
|
+
'instance_name': self.instance_name,
|
|
219
|
+
'total_operations': self.stats['total_operations']
|
|
220
|
+
})
|
|
165
221
|
|
|
166
222
|
def _submit_to_mysql(self):
|
|
167
223
|
"""同步提交统计数据到MySQL"""
|
|
@@ -171,15 +227,17 @@ class CacheStatsCollector:
|
|
|
171
227
|
stats_data = self.get_stats()
|
|
172
228
|
if not stats_data.get('enabled'):
|
|
173
229
|
return
|
|
230
|
+
|
|
231
|
+
db_name = self.config.get('db_name', 'redis_stats')
|
|
232
|
+
table_name = self.config.get('table_name', 'cache_performance')
|
|
174
233
|
|
|
175
234
|
try:
|
|
176
235
|
connection = self.mysql_pool.connection()
|
|
177
236
|
with connection.cursor() as cursor:
|
|
178
237
|
# 选择数据库
|
|
179
|
-
cursor.execute(f"USE `{
|
|
238
|
+
cursor.execute(f"USE `{db_name}`")
|
|
180
239
|
|
|
181
240
|
# 插入统计数据
|
|
182
|
-
table_name = self.config.get('table_name', 'cache_performance')
|
|
183
241
|
insert_sql = f"""
|
|
184
242
|
INSERT INTO `{table_name}` (
|
|
185
243
|
`日期`, `实例标识`, `主机名`, `进程ID`, `统计时间`,
|
|
@@ -212,9 +270,10 @@ class CacheStatsCollector:
|
|
|
212
270
|
connection.close()
|
|
213
271
|
|
|
214
272
|
except Exception as e:
|
|
215
|
-
logger.error("MySQL
|
|
273
|
+
logger.error("MySQL提交失败", {
|
|
216
274
|
'instance_name': self.instance_name,
|
|
217
|
-
'
|
|
275
|
+
'database': db_name,
|
|
276
|
+
'table': table_name,
|
|
218
277
|
'error': str(e)
|
|
219
278
|
})
|
|
220
279
|
raise
|
|
@@ -246,6 +305,20 @@ class CacheStatsCollector:
|
|
|
246
305
|
'process_id': self.process_id
|
|
247
306
|
}
|
|
248
307
|
|
|
308
|
+
def shutdown(self):
|
|
309
|
+
"""关闭统计收集器,停止后台定时器"""
|
|
310
|
+
logger.info("关闭统计收集器", {
|
|
311
|
+
'instance_name': self.instance_name
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
# 设置关闭标志
|
|
315
|
+
self._shutdown_event.set()
|
|
316
|
+
|
|
317
|
+
# 取消定时器
|
|
318
|
+
if self._timer is not None:
|
|
319
|
+
self._timer.cancel()
|
|
320
|
+
self._timer = None
|
|
321
|
+
|
|
249
322
|
def reset_stats(self):
|
|
250
323
|
"""重置统计数据"""
|
|
251
324
|
if not self.enabled:
|
|
@@ -605,7 +678,7 @@ class SmartCacheSystem:
|
|
|
605
678
|
r'.*table.*|.*column.*|.*field.*': 1200, # 20分钟
|
|
606
679
|
|
|
607
680
|
# 数据查询相关 - 变化频率中等,TTL中等
|
|
608
|
-
r'.*data.*|.*query.*|.*result.*':
|
|
681
|
+
r'.*data.*|.*query.*|.*result.*': 600, # 10分钟
|
|
609
682
|
|
|
610
683
|
# 用户会话相关 - 变化频率高,TTL较短
|
|
611
684
|
r'.*session.*|.*user.*|.*auth.*': 900, # 15分钟
|
|
@@ -614,7 +687,7 @@ class SmartCacheSystem:
|
|
|
614
687
|
r'.*config.*|.*setting.*|.*param.*': 3600, # 1小时
|
|
615
688
|
|
|
616
689
|
# 统计相关 - 变化频率中等,TTL中等
|
|
617
|
-
r'.*stats.*|.*metric.*|.*count.*':
|
|
690
|
+
r'.*stats.*|.*metric.*|.*count.*': 720, # 12分钟
|
|
618
691
|
|
|
619
692
|
# 缓存相关 - 变化频率高,TTL较短
|
|
620
693
|
r'.*cache.*|.*temp.*|.*tmp.*': 180, # 3分钟
|
|
@@ -650,12 +723,20 @@ class SmartCacheSystem:
|
|
|
650
723
|
# 3. 基于数据大小的调整
|
|
651
724
|
size_factor = 1.0
|
|
652
725
|
if data_size > 0:
|
|
653
|
-
if data_size > 1024 * 1024: # >
|
|
654
|
-
size_factor =
|
|
726
|
+
if data_size > 100 * 1024 * 1024: # > 100MB,延长TTL减少重建开销
|
|
727
|
+
size_factor = 10.0
|
|
728
|
+
elif data_size > 20 * 1024 * 1024: # > 20MB,延长TTL减少重建开销
|
|
729
|
+
size_factor = 8.0
|
|
730
|
+
elif data_size > 10 * 1024 * 1024: # > 10MB,延长TTL减少重建开销
|
|
731
|
+
size_factor = 6.0
|
|
732
|
+
elif data_size > 5 * 1024 * 1024: # > 5MB,延长TTL减少重建开销
|
|
733
|
+
size_factor = 4.0
|
|
734
|
+
elif data_size > 1024 * 1024: # > 1MB,延长TTL减少重建开销
|
|
735
|
+
size_factor = 3.0
|
|
655
736
|
elif data_size > 100 * 1024: # > 100KB
|
|
656
|
-
size_factor =
|
|
737
|
+
size_factor = 2.0
|
|
657
738
|
elif data_size < 1024: # < 1KB,缩短TTL减少内存占用
|
|
658
|
-
size_factor = 0
|
|
739
|
+
size_factor = 1.0
|
|
659
740
|
|
|
660
741
|
# 4. 计算基础TTL
|
|
661
742
|
base_ttl = self.config.default_ttl
|
|
@@ -760,8 +841,8 @@ class SmartCacheSystem:
|
|
|
760
841
|
self._state = CacheSystemState.SHUTDOWN
|
|
761
842
|
|
|
762
843
|
if self.stats_collector:
|
|
763
|
-
#
|
|
764
|
-
self.stats_collector.
|
|
844
|
+
# 关闭统计收集器(包括后台定时器)
|
|
845
|
+
self.stats_collector.shutdown()
|
|
765
846
|
|
|
766
847
|
logger.info("缓存系统已关闭", {'instance_name': self.instance_name})
|
|
767
848
|
|
mdbq/route/monitor.py
CHANGED
|
@@ -17,10 +17,10 @@ import pymysql
|
|
|
17
17
|
import hashlib
|
|
18
18
|
import functools
|
|
19
19
|
from datetime import datetime, timedelta
|
|
20
|
-
from typing import Dict, Any
|
|
21
|
-
from urllib.parse import urlparse
|
|
22
|
-
from dbutils.pooled_db import PooledDB
|
|
23
|
-
from mdbq.myconf import myconf
|
|
20
|
+
from typing import Dict, Any
|
|
21
|
+
from urllib.parse import urlparse
|
|
22
|
+
from dbutils.pooled_db import PooledDB # type: ignore
|
|
23
|
+
from mdbq.myconf import myconf # type: ignore
|
|
24
24
|
from mdbq.log import mylogger
|
|
25
25
|
from flask import request, g
|
|
26
26
|
import re
|
|
@@ -754,7 +754,20 @@ class RouteMonitor:
|
|
|
754
754
|
process_time = round((end_time - start_time) * 1000, 3)
|
|
755
755
|
|
|
756
756
|
response_status = getattr(response, 'status_code', 200) if hasattr(response, 'status_code') else 200
|
|
757
|
-
|
|
757
|
+
|
|
758
|
+
# 🚀 兼容流式响应:检查是否为直通模式
|
|
759
|
+
response_size = 0
|
|
760
|
+
try:
|
|
761
|
+
if hasattr(response, 'direct_passthrough') and response.direct_passthrough:
|
|
762
|
+
# 流式响应模式,无法获取准确大小,使用估算值
|
|
763
|
+
response_size = -1 # 标记为流式响应
|
|
764
|
+
elif hasattr(response, 'get_data'):
|
|
765
|
+
response_size = len(str(response.get_data()))
|
|
766
|
+
else:
|
|
767
|
+
response_size = 0
|
|
768
|
+
except (RuntimeError, Exception):
|
|
769
|
+
# 如果获取数据失败(如直通模式),使用默认值
|
|
770
|
+
response_size = -1 # 标记为无法获取大小
|
|
758
771
|
|
|
759
772
|
response_data = {
|
|
760
773
|
'response_status': response_status,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
|
2
|
-
mdbq/__version__.py,sha256=
|
|
2
|
+
mdbq/__version__.py,sha256=PoAOdx_PmyCqgx7t2JxhQUunBadMGsH3LujMSNP6cyM,19
|
|
3
3
|
mdbq/auth/__init__.py,sha256=pnPMAt63sh1B6kEvmutUuro46zVf2v2YDAG7q-jV_To,24
|
|
4
4
|
mdbq/auth/auth_backend.py,sha256=-6PyJNwGnZd8BwFGnOn3396tj2fxy1uP5K1f0gC0zow,86562
|
|
5
5
|
mdbq/auth/rate_limiter.py,sha256=1m_Paxp8pDNpmyoFGRpFMVOJpbmeIvfVcfiQ2oH72qM,32850
|
|
@@ -26,15 +26,15 @@ mdbq/pbix/pbix_refresh.py,sha256=JUjKW3bNEyoMVfVfo77UhguvS5AWkixvVhDbw4_MHco,239
|
|
|
26
26
|
mdbq/pbix/refresh_all.py,sha256=OBT9EewSZ0aRS9vL_FflVn74d4l2G00wzHiikCC4TC0,5926
|
|
27
27
|
mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
|
|
28
28
|
mdbq/redis/getredis.py,sha256=vpBuNc22uj9Vr-_Dh25_wpwWM1e-072EAAIBdB_IpL0,23494
|
|
29
|
-
mdbq/redis/redis_cache.py,sha256=
|
|
29
|
+
mdbq/redis/redis_cache.py,sha256=2uhAmG9FYHjzByZEg7o8fFFxuF6AnOASVvjdAM4Gx5E,35719
|
|
30
30
|
mdbq/route/__init__.py,sha256=BT_dAY7V-U2o72bevq1B9Mq9QA7GodwtkxyLNdGaoE8,22
|
|
31
31
|
mdbq/route/analytics.py,sha256=dngj5hVwKddEUy59nSYbOoJ9C7OVrtCmCkvW6Uj9RYM,28097
|
|
32
|
-
mdbq/route/monitor.py,sha256=
|
|
32
|
+
mdbq/route/monitor.py,sha256=8uscuoJF4eMr5o8cAqywQv868m1yIGCuK1l4ZWN6KPE,42954
|
|
33
33
|
mdbq/route/routes.py,sha256=QVGfTvDgu0CpcKCvk1ra74H8uojgqTLUav1fnVAqLEA,29433
|
|
34
34
|
mdbq/selenium/__init__.py,sha256=AKzeEceqZyvqn2dEDoJSzDQnbuENkJSHAlbHAD0u0ZI,10
|
|
35
35
|
mdbq/selenium/get_driver.py,sha256=1NTlVUE6QsyjTrVVVqTO2LOnYf578ccFWlWnvIXGtic,20903
|
|
36
36
|
mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
|
|
37
|
-
mdbq-4.0.
|
|
38
|
-
mdbq-4.0.
|
|
39
|
-
mdbq-4.0.
|
|
40
|
-
mdbq-4.0.
|
|
37
|
+
mdbq-4.0.117.dist-info/METADATA,sha256=wTTsk7ZB4zxhXnlAG8PvZ69aXGa63gs7eaNs27sKY8s,365
|
|
38
|
+
mdbq-4.0.117.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
39
|
+
mdbq-4.0.117.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
|
40
|
+
mdbq-4.0.117.dist-info/RECORD,,
|
|
File without changes
|