Files
SMANAGE/gvsds/apps/stats/tasks.py
T
TermiNexus 727fbba272 fix(tasks): stats 任务套重试 + PurgeOld 全周期清理;攻击日志写库限频去重
- 四个 stats 任务加 RetryOnFailure(2 次重试)
- PurgeOldStats 清理 daily/monthly/hourly 全周期
- AttackDetectMiddleware 同 IP+类型 60s 窗口去重,防 401 刷屏反向写库放大
2026-08-13 17:55:56 +08:00

110 lines
4.1 KiB
Python

"""模块:统计引擎 Celery 任务
将统计引擎的定时清零、过期清理、排行榜生成接入 Celery beat 调度。
通过 gvsds/celery.py 的 beat_schedule 配置定时触发。
无 Celery 时可通过 management 命令或外部 cron 手动调用。
统一走 gvsdsdk StatsManager 编排方法,避免双轨实现。
"""
import logging
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 周期数据(默认清零昨天)。
建议 beat 调度:每日 00:00 执行。
"""
from gvsdsdk.stats.manager import StatsManager
total = 0
for tenant_uuid in _GetActiveTenants():
try:
total += StatsManager.RunScheduledResets(tenant_uuid)
except Exception:
logger.warning('[StatsTasks] ResetDaily 失败: tenant=%s', tenant_uuid, exc_info=True)
logger.info('[StatsTasks] ResetDailyStats 完成,重置 %d 条', total)
return {'reset_count': total}
@shared_task(bind=True, name='stats.ResetMonthlyStats')
@RetryOnFailure(max_retries=2, delay=5)
def ResetMonthlyStats(self):
"""每月统计清零任务——重置所有租户的 monthly 周期数据(默认清零上月)。
建议 beat 调度:每月 1 日 00:00 执行。
"""
from gvsdsdk.stats.manager import StatsManager
from gvsdsdk.stats.models import StatConfig
total = 0
for tenant_uuid in _GetActiveTenants():
for config in StatConfig.objects.filter(
TenantUUID=tenant_uuid, AutoReset=True, ConfigStatus=1):
try:
total += StatsManager.MonthlyReset(tenant_uuid, config.Metric_id)
except Exception:
logger.warning('[StatsTasks] ResetMonthly 失败: tenant=%s metric=%s', tenant_uuid, config.Metric_id, exc_info=True)
logger.info('[StatsTasks] ResetMonthlyStats 完成,重置 %d 条', total)
return {'reset_count': total}
@shared_task(bind=True, name='stats.PurgeOldStats')
def PurgeOldStats(self, retention_days=90):
"""清理过期统计数据——删除 N 天前的 daily 记录。
建议 beat 调度:每日 03:00 执行(避开清零高峰)。
"""
from gvsdsdk.stats.manager import StatsManager
before = date.today() - timedelta(days=retention_days)
total = 0
for tenant_uuid in _GetActiveTenants():
try:
total += StatsManager.PurgeOld(tenant_uuid, before, period_type='daily')
except Exception:
logger.warning('[StatsTasks] PurgeOld 失败: tenant=%s', tenant_uuid, exc_info=True)
logger.info('[StatsTasks] PurgeOldStats 完成,清理 %d 条(保留 %d 天)', total, retention_days)
return {'purge_count': total, 'before_date': before.isoformat()}
@shared_task(bind=True, name='stats.BuildDailyRankings')
@RetryOnFailure(max_retries=2, delay=5)
def BuildDailyRankings(self):
"""生成每日排行榜——为所有启用排行的指标生成当日排行。
建议 beat 调度:每日 23:50 执行(当日数据基本定型)。
"""
from gvsdsdk.stats.manager import StatsManager
total = 0
for tenant_uuid in _GetActiveTenants():
try:
total += StatsManager.BuildRankings(tenant_uuid)
except Exception:
logger.warning('[StatsTasks] BuildRanking 失败: tenant=%s', tenant_uuid, exc_info=True)
logger.info('[StatsTasks] BuildDailyRankings 完成,生成 %d 个排行', total)
return {'ranking_count': total}
def _GetActiveTenants():
"""获取所有活跃租户的 UUID 列表(bytes 形式)。"""
try:
from gvsdsdk.models import Tenant
return [t.TenantUUID.bytes for t in Tenant.objects.filter(TenantStatus=1)]
except Exception:
logger.warning('[StatsTasks] 获取租户列表失败,返回空列表', exc_info=True)
return []