"""限流器 — 滑动窗口内存计数器。 App1 按 user_id 限流(每用户每分钟 10 次), App2~8 按 site_id 限流(每门店每小时 100 次)。 内存实现,单实例部署,不依赖外部存储。 """ from __future__ import annotations import time from collections import deque class RateLimiter: """滑动窗口内存限流器。 - check_user_rate():App1 每用户每分钟限流 - check_store_rate():App2~8 每门店每小时限流 每个 key 维护一个时间戳 deque,检查时先清除过期条目, 再判断窗口内请求数是否低于阈值。 """ def __init__(self) -> None: self._user_windows: dict[str, deque[float]] = {} # App1: user_id → 时间戳队列 self._store_windows: dict[str, deque[float]] = {} # App2~8: site_id → 时间戳队列 def _check( self, windows: dict[str, deque[float]], key: str, limit: int, window_seconds: int, ) -> bool: """通用滑动窗口检查。返回 True 表示允许。""" now = time.monotonic() if key not in windows: windows[key] = deque() window = windows[key] # 清除窗口外的过期时间戳 cutoff = now - window_seconds while window and window[0] <= cutoff: window.popleft() # 判断是否超限 if len(window) >= limit: return False # 未超限,记录本次请求时间戳 window.append(now) return True def check_user_rate( self, user_id: str, limit: int = 10, window_seconds: int = 60, ) -> bool: """App1 每用户每分钟限流。返回 True 表示允许。""" return self._check(self._user_windows, user_id, limit, window_seconds) def check_store_rate( self, site_id: int, limit: int = 100, window_seconds: int = 3600, ) -> bool: """App2~8 每门店每小时限流。返回 True 表示允许。""" # site_id 为 int,转为 str 作为 dict key return self._check(self._store_windows, str(site_id), limit, window_seconds)