You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在会话结束前修改Session值以实现页面活跃用户数统计

嘿,我懂你现在要做的是统计页面上的活跃用户数,之前的代码思路其实有点跑偏了,而且确实卡在了「关闭会话时没法减少计数」的问题上。先帮你拆解下现有代码的问题,再给你落地的解决方案~

先说说现有代码的问题

你现在的sender.php是给每个用户自己的Session变量foo加1,这其实统计的是单个用户的访问次数,不是全局的活跃用户数;而且receiver.php里直接给$_POST['foo']赋值的写法也不对,$_POST是用来接收前端提交的数据的,不该直接赋值。

更关键的是:PHP的Session在用户关闭浏览器时,服务器不会收到即时通知——Session是靠客户端的Cookie来维持的,用户关浏览器后Cookie失效,但服务器端的Session文件要等PHP的垃圾回收机制触发才会清理,所以没法做到「用户一关浏览器就立刻减计数」。

正确的核心思路

要统计活跃用户数,你需要一个全局的存储容器(比如数据库、Redis)来记录所有活跃用户的标识(比如Session ID),然后通过「定期清理超时用户」的方式来维护准确的计数:

  1. 用户访问页面时,把他的Session ID存入全局存储,并更新最后活跃时间
  2. 定期清理掉「超过N分钟(比如30分钟)没有活跃」的用户记录
  3. 全局存储里剩下的记录数,就是当前的活跃用户数

具体实现方案

下面给你两种常用的实现方式,你可以根据自己的环境选择:

方案1:用Redis实现(推荐,性能更高)

Redis的有序集合或者集合非常适合做这个场景,操作快还自带过期/排序能力。

步骤1:页面入口(比如每个页面都要引入的init.php

session_start();

// 连接Redis(确保服务器已经安装Redis和PHP的Redis扩展)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// 把当前用户的Session ID存入有序集合,分值是当前时间戳(用来记录最后活跃时间)
$redis->zAdd('active_users', time(), session_id());

// 清理掉30分钟(1800秒)内没有活跃的用户
$redis->zRemRangeByScore('active_users', 0, time() - 1800);

// 现在就能拿到准确的活跃用户数了
$activeUserCount = $redis->zCard('active_users');

步骤2:显示活跃用户数的页面(比如receiver.php

session_start();

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// 先清理超时用户,保证计数准确
$redis->zRemRangeByScore('active_users', 0, time() - 1800);
$activeUserCount = $redis->zCard('active_users');

echo '<p style="font-size:80px">' . $activeUserCount . '</p>';

方案2:用MySQL数据库实现

如果没有Redis,用数据库也能实现,只是性能稍差一点。

步骤1:先建表

执行以下SQL创建存储活跃用户的表:

CREATE TABLE active_users (
    session_id VARCHAR(255) PRIMARY KEY COMMENT '用户会话ID',
    last_active INT UNSIGNED NOT NULL COMMENT '最后活跃时间戳'
);

步骤2:页面入口

session_start();

// 连接数据库(替换成你的数据库信息)
$pdo = new PDO('mysql:host=localhost;dbname=your_database_name', 'your_username', 'your_password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 插入或更新当前用户的最后活跃时间
$stmt = $pdo->prepare("
    INSERT INTO active_users (session_id, last_active) 
    VALUES (?, ?) 
    ON DUPLICATE KEY UPDATE last_active = ?
");
$stmt->execute([session_id(), time(), time()]);

// 清理30分钟内没有活跃的用户
$stmt = $pdo->prepare("DELETE FROM active_users WHERE last_active < ?");
$stmt->execute([time() - 1800]);

// 获取活跃用户数
$stmt = $pdo->query("SELECT COUNT(*) FROM active_users");
$activeUserCount = $stmt->fetchColumn();

步骤3:显示活跃用户数的页面

session_start();

$pdo = new PDO('mysql:host=localhost;dbname=your_database_name', 'your_username', 'your_password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 先清理超时用户
$stmt = $pdo->prepare("DELETE FROM active_users WHERE last_active < ?");
$stmt->execute([time() - 1800]);

// 统计并显示
$stmt = $pdo->query("SELECT COUNT(*) FROM active_users");
$activeUserCount = $stmt->fetchColumn();

echo '<p style="font-size:80px">' . $activeUserCount . '</p>';

处理用户主动注销的情况

如果你的网站有「退出登录」按钮,那可以在注销脚本里主动移除该用户的记录,让计数立刻更新:

session_start();

// Redis版本
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->zRem('active_users', session_id());

// 数据库版本(如果用数据库的话就注释掉上面,用下面的代码)
// $pdo = new PDO('mysql:host=localhost;dbname=your_database_name', 'your_username', 'your_password');
// $stmt = $pdo->prepare("DELETE FROM active_users WHERE session_id = ?");
// $stmt->execute([session_id()]);

// 销毁会话
session_destroy();
// 跳转到首页或登录页
header("Location: index.php");
exit;

内容的提问来源于stack exchange,提问作者Zakaria Ben Yahya

火山引擎 最新活动