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

PHP 統計在線人數實現方案:Session+文件存儲與現代化 Redis 改造

     編輯:PHP 相關知識

PHP 統計在線人數實現方案:Session+文件存儲與現代化 Redis 改造

統計在線人數是網站運營基礎指標。本文解析基於 Session + 文件存儲的經典實現,指出其併發與擴展瓶頸,提供 Redis 現代化改造方案與多維度統計擴展思路。

經典實現:Session + 文件存儲

 $lastTime) {
            fwrite($fp, "{$sessionId}|{$lastTime}\n");
        }
        flock($fp, LOCK_UN);
    }
    fclose($fp);
}

// 6. 輸出供 JS 調用(文檔寫入)
echo "document.write('當前在線:{$total_online} 人');";
?>

方案缺陷深度分析

維度問題影響
併發性能文件獨寫鎖(LOCK_EX)序列化所有請求QPS 受限 ~100-200,高併發阻塞嚴重
擴展性單機文件,多服務器無法共享負載均衡場景下數據不一致
準確性依賴 Cookie,用戶禁用/清除 Cookie 導致重複計數統計偏差大,無法識別同一用戶多設備
清理機制每請求掃描全文件清理過期文件增大時 IO 開銷線性增長
數據結構純文本行解析,無索引、無聚合無法按頁面/地域/設備維度統計

現代化改造:Redis 高性能方案

核心數據結構設計

class OnlineCounter {
    private \Redis $redis;
    private int $ttl = 600; // 10分鐘在線窗口
    
    public function __construct(\Redis $redis) {
        $this->redis = $redis;
    }
    
    /** 記錄用戶活躍(每請求調用) */
    public function recordActivity(string $userId, array $metadata = []): void {
        $key = "online:users:{$userId}";
        $now = time();
        
        // Pipeline 批量操作,減少 RTT
        $pipe = $this->redis->pipeline();
        
        // 1. 更新用戶最後活躍時間(Sorted Set,score=timestamp)
        $pipe->zAdd("online:active", $now, $userId);
        
        // 2. 設置用戶元數據(Hash:IP、UA、頁面、地域等)
        if ($metadata) {
            $pipe->hSet("online:meta:{$userId}", $metadata);
            $pipe->expire("online:meta:{$userId}", $this->ttl * 2);
        }
        
        // 3. 頁面級統計(可選)
        if (isset($metadata['page'])) {
            $pipe->zIncrBy("online:page:{$metadata['page']}", 1, $userId);
        }
        
        // 4. 設置用戶級 TTL(自動過期清理)
        $pipe->expire($key, $this->ttl);
        
        $pipe->execute();
    }
    
    /** 獲取全站在線人數 */
    public function getTotalOnline(): int {
        return $this->redis->zCount("online:active", time() - $this->ttl, '+inf');
    }
    
    /** 獲取指定頁面在線人數 */
    public function getPageOnline(string $page): int {
        return $this->redis->zCount("online:page:{$page}", time() - $this->ttl, '+inf');
    }
    
    /** 獲取在線用戶列表(分頁) */
    public function getOnlineUsers(int $page = 1, int $perPage = 50): array {
        $start = ($page - 1) * $perPage;
        $userIds = $this->redis->zRevRange("online:active", $start, $start + $perPage - 1);
        $users = [];
        foreach ($userIds as $uid) {
            $meta = $this->redis->hGetAll("online:meta:{$uid}");
            $meta['id'] = $uid;
            $meta['last_active'] = $this->redis->zScore("online:active", $uid);
            $users[] = $meta;
        }
        return $users;
    }
    
    /** 定時任務:清理過期數據(每分鐘跑一次) */
    public function cleanupExpired(): int {
        $removed = $this->redis->zRemRangeByScore("online:active", 0, time() - $this->ttl);
        // 元數據由 TTL 自動過期,無需手動清理
        return $removed;
    }
}

Nginx + Lua 邊緣統計(極致性能)

# nginx.conf http 段
lua_shared_dict online_users 10m;

# 在 location / 內
access_by_lua_block {
    local redis = require "resty.redis"
    local red = redis:new()
    red:set_timeout(10)
    red:connect("127.0.0.1", 6379)
    
    local user_id = ngx.var.cookie_user_id or ngx.md5(ngx.var.remote_addr .. ngx.var.http_user_agent)
    local now = ngx.time()
    local ttl = 600
    
    -- 更新全站活躍集合
    red:zadd("online:active", now, user_id)
    red:expire("online:active", ttl * 2)
    
    -- 更新頁面統計
    local page = ngx.var.uri
    red:zincrby("online:page:" .. page, 1, user_id)
    red:expire("online:page:" .. page, ttl * 2)
    
    red:set_keepalive(10000, 100)
}

多維度統計擴展設計

維度Redis Key 設計統計指標應用場景
全站總覽online:active (ZSET)總在線、峰值、趨勢圖運營大屏、實時監控
頁面熱度online:page:{uri} (ZSET)頁面在線、停留分布內容優化、廣告定價
地域分布online:geo:{country}/{province} (ZSET)地理分佈、CDN 調度多機房部署、合規審計
設備類型online:device:{mobile/desktop/tablet} (ZSET)終端佔比、適配優先級響應式設計、App 推廣
用戶層級online:level:{vip/normal/guest} (ZSET)付費用戶活躍度運營活動、流失預警
實時地圖online:geoip (GEOADD)經緯度熱力圖大屏展示、應急響應

生產級部署檢查清單

  1. Redis 高可用:主從 + Sentinel / Cluster 模式,防單點故障
  2. Key 過期策略:配置 maxmemory-policy allkeys-lru,防內存溢出
  3. 連接池管理:PHP-FPM 配合 pconnect 或 Swoole 協程池,避免連接風暴
  4. 時鐘同步:所有節點 NTP 同步,保證時間窗口一致性
  5. 降級方案:Redis 不可用時回退到本地內存/文件,保證統計不中斷
  6. 隱私合規:IP 脫敏、用戶 ID 雜湊、GDPR/個保法合規清理

從文件存儲到 Redis 再到 Nginx Lua 邊緣統計,架構演進的核心是將狀態從計算節點剝離到共享存儲,再下沉到流量入口。根據業務規模選擇合適層級:日均萬級用戶用 Redis 方案即可;億級流量需邊緣統計 + 流式計算(Flink/ClickHouse)實現秒級多維聚合。

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