首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP + Memcached 實現頁面防刷新限流:原子計數器與滑動窗口實戰

     編輯:PHP 綜合知識

需求場景

防止用戶短時間內瘋狂刷新頁面(如 1 分鐘超 100 次),保護服務器資源,防惡意刷接口。

核心設計:Memcached 原子計數器 + TTL 滑動窗口

  • Key 設計:`IP:文件名`(如 `C1234567890index.php`)
  • 計數器:`increment()` 原子自增,避免競態條件
  • 過期時間:`set()` 時指定 `60s`,自動實現滑動窗口
  • 閾值判斷:超過 100 直接 `die()` 拒絕

完整實現代碼

get($key);
    
    if ($current !== false) {
        // Key 存在:檢查閾值
        if ($current >= $maxRequests) {
            // 觸發限流:返回 429 狀態碼 + 友好提示
            http_response_code(429);
            header('Content-Type: text/html; charset=utf-8');
            exit('

請求過於頻繁,請稍後再試 (429 Too Many Requests)

'); } // 未超限:原子遞增 $memcache->increment($key); } else { // Key 不存在:首次訪問,設置初始值 1,TTL 60s // Memcache::set($key, $var, $flag, $expire) $memcache->set($key, 1, 0, $windowSeconds); } } /** * 獲取客戶端真實 IP(支持代理) */ function getClientIp(): string { $headers = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP']; foreach ($headers as $header) { if (!empty($_SERVER[$header])) { $ips = explode(',', $_SERVER[$header]); return trim($ips[0]); // 取第一個非代理 IP } } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } // 在項目公共入口文件調用 // requestRateLimit(100, 60);

關鍵點深度解析

1. 為什麼用 `sprintf('%u', ip2long())`?

`ip2long()` 返回有符號整數,負數無法作為 Key 一部分。`sprintf('%u')` 轉為無符號字符串,保證 Key 唯一且可讀。

2. `increment()` 的原子性保證

Memcached 協議層面保證 `incr`/`decr` 原子操作,多進程並發下計數絕不會漏增或重複增。

3. TTL 實現滑動窗口的局限

當前方案是固定窗口:Key 創建後 60s 過期。邊界瞬間(如 59.9s 和 60.1s)可能允許 200 次請求。
嚴格滑動窗口需用 Redis Sorted Set(時間戳為分數)或 Lua 腳本實現。

4. 降級策略

Memcached 故障時 不應阻斷正常業務,代碼中 `return` 而非 `die()`,僅記錄錯誤日誌。

進階優化:Redis 版本(推薦生產環境)

// Redis + Lua 腳本實現精確滑動窗口
$luaScript = <<= limit then
    return 0
end
redis.call('ZADD', key, now, now .. '-' .. math.random())
redis.call('EXPIRE', key, window)
return 1
LUA;

$allowed = $redis->eval($luaScript, 1, $key, $maxRequests, $windowSeconds, time() * 1000);
if (!$allowed) { /* 429 */ }

部署檢查清單

  • [ ] Memcached 服務可用、內存足夠(`stats` 監控)
  • [ ] PHP `memcache` 擴展安裝正確(非 `memcached` 擴展,API 不同)
  • [ ] 代理層(Nginx/Cloudflare)正確傳遞 `X-Forwarded-For`
  • [ ] 靜態資源(CSS/JS/圖片)排除限流,避免頁面加載觸發誤判
  • [ ] 監控告警:限流觸發率、Memcached 命中率、錯誤率

這套方案以極低延遲、極簡代碼解決了 90% 的頁面刷新濫用場景。高併發核心業務建議遷移到 Redis + Lua 精確滑動窗口,並接入 API 网關層統一治理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved