fix(tasks): stats 任务套重试 + PurgeOld 全周期清理;攻击日志写库限频去重
- 四个 stats 任务加 RetryOnFailure(2 次重试) - PurgeOldStats 清理 daily/monthly/hourly 全周期 - AttackDetectMiddleware 同 IP+类型 60s 窗口去重,防 401 刷屏反向写库放大
This commit is contained in:
1 parent
221c5d9e9d
commit
727fbba272
2 files changed
+26
No files matched your search
@@ -12,10 +12,16 @@ from datetime import date, timedelta
|
||||
|
||||
from celery import shared_task
|
||||
|
||||
from gvsds.celery_utils import RetryOnFailure
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#: PurgeOld 清理的周期类型(daily 每日 / monthly 每月 / hourly 每小时)
|
||||
_PURGE_PERIOD_TYPES = ('daily', 'monthly', 'hourly')
|
||||
|
||||
|
||||
@shared_task(bind=True, name='stats.ResetDailyStats')
|
||||
@RetryOnFailure(max_retries=2, delay=5)
|
||||
def ResetDailyStats(self):
|
||||
"""每日统计清零任务——重置所有租户的 daily 周期数据(默认清零昨天)。
|
||||
|
||||
@@ -34,6 +40,7 @@ def ResetDailyStats(self):
|
||||
|
||||
|
||||
@shared_task(bind=True, name='stats.ResetMonthlyStats')
|
||||
@RetryOnFailure(max_retries=2, delay=5)
|
||||
def ResetMonthlyStats(self):
|
||||
"""每月统计清零任务——重置所有租户的 monthly 周期数据(默认清零上月)。
|
||||
|
||||
@@ -74,6 +81,7 @@ def PurgeOldStats(self, retention_days=90):
|
||||
|
||||
|
||||
@shared_task(bind=True, name='stats.BuildDailyRankings')
|
||||
@RetryOnFailure(max_retries=2, delay=5)
|
||||
def BuildDailyRankings(self):
|
||||
"""生成每日排行榜——为所有启用排行的指标生成当日排行。
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import re
|
||||
import uuid
|
||||
import time as _time
|
||||
import logging
|
||||
from gvsds.app_config import getlist as _app_getlist, get as _app_get, getboolean as _app_getbool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _monotonic():
|
||||
"""单调时钟(性能计数,不受系统时间调整影响)"""
|
||||
return _time.monotonic()
|
||||
|
||||
|
||||
class DomainMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
@@ -114,6 +120,8 @@ class AttackDetectMiddleware:
|
||||
p.strip() for p in _app_getlist('attack_detect', 'EXEMPT_PATHS', fallback=['/API/Auth', '/health'])
|
||||
)
|
||||
self.max_body_len = int(_app_get('attack_detect', 'MAX_BODY_LEN', fallback='1024'))
|
||||
#: 攻击日志限频去重表:(ip, attempt_type) -> 最近写入时间(monotonic)
|
||||
self._recent_attacks = {}
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
@@ -158,6 +166,16 @@ class AttackDetectMiddleware:
|
||||
|
||||
attempt_type = self._classify(response.status_code)
|
||||
|
||||
# 限频去重:同 (IP, 类型) 60s 窗口只记录一次,防攻击者刷 401 反向写库放大
|
||||
now = _monotonic()
|
||||
dedup_key = (request.META.get('REMOTE_ADDR', ''), attempt_type)
|
||||
last_write = self._recent_attacks.get(dedup_key, 0)
|
||||
if now - last_write < 60:
|
||||
return
|
||||
self._recent_attacks[dedup_key] = now
|
||||
if len(self._recent_attacks) > 10000: # 防内存无界增长
|
||||
self._recent_attacks.clear()
|
||||
|
||||
AbnormalUserLog.objects.using('gerpcore').create(
|
||||
LogUUID=uuid.uuid4().bytes,
|
||||
TenantUUID=tenant_uuid,
|
||||
|
||||
Reference in new issue
Block a user