• 技术文章 >后端开发 >php教程

    用PHP来统计在线人数的四个方法详解

    长期闲置长期闲置2022-06-08 13:51:38转载1942
    本篇文章给大家带来了关于PHP的相关知识,其中主要介绍了怎么实现统计在线人数的问题,可以利用表统计方式、用redis有序集合统计、用hyperloglog做统计等等,下面一起来看一下,希望对大家有帮助。

    推荐学习:《PHP视频教程

    一个业务系统网站每天人数的访问量是多少,在线人数是多少?这种业务我们在开发中就要预留,也是在我们的设计范围内的咯!因为一个正在运营的网站,每天都会用到统计。

    那在线人数是如何统计的呢,这里有几种方案,代码用 laravel 框架。可以作为开发中参考。

    1 用表统计方式

    用数据表统计在线人数,这种方式只能用在并发量不大的情况下。

    首先我们先新建表:user_login

    20.png

    编辑

    user_login 表

    模拟用户登录,不存在用户就存入表,存在的则更新登录信息

    // 客户端唯一的识别码
    $client_id = session()->getId();
    //用户是否已存在
    $user = DB::table('user_login')
        ->where('token', $client_id)
        ->first();
    //不存在则插入数据
    if (empty($user)) {
        $data = [
            'token' => $client_id,
            'username' => 'user_' . $client_id, // 模拟用户
            'uid' => mt_rand(10000000, 99999999),   //模拟用户id
            'create_time' => date('Y-m-d H:i:s'),
            'update_time' => date('Y-m-d H:i:s')
        ];
        DB::table('user_login')->insert($data);
    } else {    
        // 存在则更新用户登录信息
        DB::table('user_login')
         ->where('token', $client_id)
         ->update([
              'update_time' => date('Y-m-d H:i:s')
          ]);
    }

    这里还需要定期清理无任何操作的用户,假如用户一个小时内无任何操作,我们可以记为无效用户

    代码如下:

    // 客户端唯一的识别码
    $client_id = session()->getId();
    //用户是否已存在
    $user = DB::table('user_login')
        ->where('token', $client_id)
        ->first();
    //不存在则插入数据
    if (empty($user)) {
        $data = [
            'token' => $client_id,
            'username' => 'user_' . $client_id, // 模拟用户
            'uid' => mt_rand(10000000, 99999999),   //模拟用户id
            'create_time' => date('Y-m-d H:i:s'),
            'update_time' => date('Y-m-d H:i:s')
        ];
        DB::table('user_login')->insert($data);
    } else {    
        // 存在则更新用户登录信息
        DB::table('user_login')
         ->where('token', $client_id)
         ->update([
              'update_time' => date('Y-m-d H:i:s')
          ]);
    }

    我们可以实现的功能:

    1)当前在线人数

    2)某时间段内在线人数

    3)最新上线的用户

    4)指定用户是否在线

    // 可实现功能一:当前总共在线人数
    $c = DB::table('user_login')->count();
    echo '当前在线人数:' . $c . '<br />';
    // 可实现功能二:某时间段内在线人数
    $begin_date = '2020-08-13 09:00:00';
    $end_date = '2020-08-13 18:00:00';
    $c = DB::table('user_login')
        ->where('create_time', '>=', $begin_date)
        ->where('create_time', '<=', $end_date)
        ->count();
    echo $begin_date . '-' . $end_date . '在线人数:' . $c . '<br />';
    // 可实现功能三:最新上线的用户
    $newest = DB::table('user_login')
        ->orderBy('create_time', 'DESC')
        ->limit(10)
        ->get();
    echo '最新上线的用户有:';
    foreach ($newest as $value) {
        echo $value->username . ' ';
    }
    echo '<br />';
    // 可实现功能四:指定用户是否在线
    $username = 'user_1111';
    $online = DB::table('user_login')
        ->where('username', $username)
        ->exists();
    echo $username . ($online ? '在线' : '不在线');

    2 使用 redis 有序集合实现在线人数统计

    因为是内存中,所以效率很高,可以统计某个时间段内的在线人数,可以做各种聚合操作。但是如果在线人数比较多的情况下,会比较占用内存。还有一点:

    无法通过用户操作时间清除掉无效用户,只有手动登出的用户才会从集合中删除。

    代码如下:

    // 客户端唯一的识别码
    $client_id = session()->getId();
    echo $client_id . '<br />';
    // 按日期生成key
    $day = date('Ymd');
    $key = 'online:' . $day;
    // 是否在线
    $is_online = Redis::zScore($key, $client_id);
    if (empty($is_online)) {    // 不在线,加入当前客户端
        Redis::zAdd($key, time(), $client_id);
    }
    // 可实现功能一:当前总共在线人数
    $c = Redis::zCard($key);
    echo '当前在线人数:' . $c . '<br />';
    // 可实现功能二:某时间段内在线人数
    $begin_date = '2020-08-13 09:00:00';
    $end_date = '2020-08-13 18:00:00';
    $c = Redis::zCount($key, strtotime($begin_date), strtotime($end_date));
    echo $begin_date . '-' . $end_date . '在线人数:' . $c . '<br />';
    // 可实现功能三:最新上线的用户,时间从小到大排序
    $newest = Redis::zRangeByScore($key, '-inf', '+inf', ['limit' => [0, 50]]);
    echo '最新上线的用户有:';
    foreach ($newest as $value) {
        echo $value . ' ';
    }
    echo '<br />';
    // 可实现功能四:指定用户是否在线
    $username = $client_id;
    $online = Redis::zScore($key, $client_id);;
    echo $username . ($online ? '在线' : '不在线') . '<br />';
    // 可实现功能五:昨天和今天都上线的客户
    $yestoday = Carbon::yesterday()->toDateString();
    $yes_key = str_replace('-', '', $yestoday);
    $members = [];
    Redis::pipeline(function ($pipe) use ($key, $yes_key, &$members) {
        Redis::zinterstore('new_key', [$key, $yes_key], ['aggregate' => 'min']);
        $members = Redis::zRangeByScore('new_key', '-inf', '+inf', ['limit' => [0, 50]]);
        //dump($members);
    });
    echo '昨天和今天都上线的用户有:';
    foreach ($members as $value) {
        echo $value . ' ';
    }

    3 使用 hyperloglog 做统计

    跟有序集合方式不同,hyperloglog 十分节约空间,但是实现的功能也非常单一,只能统计在线人数,不能实现其余的任何功能。

    Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

    在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

    但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

    // note HyperLogLog 只需要知道在线总人数
    for ($i=0; $i < 6; $i++) {
        $online_user_num = mt_rand(10000000, 99999999);     //模拟在线人数
        var_dump($online_user_num);
        for ($j=1; $j < $online_user_num; $j++) { 
            $user_id = mt_rand(1, 100000000);
            $redis->pfadd('002|online_users_day_'.$i, [$user_id]);
        }
    }
    $count = 0;
    for ($i=0; $i < 3; $i++) { 
        $count += $redis->pfcount('002|online_users_day_'.$i);
        print_r($redis->pfcount('002|online_users_day_'.$i). "\n");
    }
    var_dump($count);
    //note  3 days total online num
    var_dump($redis->pfmerge('002|online_users_day_both_3', ['002|online_users_day_0', '002|online_users_day_1', '002|online_users_day_2']));
    var_dump($redis->pfcount('002|online_users_day_both_3'));

    这种方案仅仅只能统计出某个时间段在线人数的总量, 对在线用户的名单却无能为力,但是却挺节省内存的,对统计数据要求不多情况下 ,我们便可以考虑这种方案。

    4 使用 bitmap 统计

    bitmap 就是通过一个 bit 位来表示某个元素对应的值或者状态,其中的 key 就是对应元素本身。我们知道 8 个 bit 可以组成一个 Byte,所以 bitmap 本身会极大的节省储存空间。

    bitmap 常用来做比如用户签到、活跃用户、在线用户等功能。

    代码如下

    // 模拟当前用户
    $uid = request('uid');
    $key = 'online_bitmap_' . date('Ymd');
    // 设置当前用户在线
    Redis::setBit($key, $uid, 1);
    // 可实现功能1:在线人数
    $c = Redis::bitCount($key);
    echo '在线人数:' . $c . '<br />';
    // 可实现功能2:指定用户是否在线
    $online = Redis::getBit($key, $uid);
    echo $uid . ($online ? '在线' : '不在线') . '<br />';
    // 可实现功能3:昨天和今天均上线的用户总数
    $yestoday = Carbon::yesterday()->toDateString();
    $yes_key = str_replace('-', '', $yestoday);
    $c = 0;
    Redis::pipeline(function ($pipe) use ($key, $yes_key, &$c) {
        Redis::bitOp('AND', 'yest', $key, $yes_key);
        $c = Redis::bitCount('yest');
    });
    echo '昨天和今天都上线的用户数量有:' . $c . '<br />';

    bitmap 消耗的内存空间不多, 统计的信息却挺多的,这种方案是值得推荐一下的。

    推荐学习:《PHP视频教程

    以上就是用PHP来统计在线人数的四个方法详解的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:learnku,如有侵犯,请联系admin@php.cn删除
    专题推荐:php
    上一篇:20+值得了解的PHP面试题(附答案分析) 下一篇:整理总结nginx、php-fpm和mysql等的权限划分
    20期PHP线上班

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• php怎么去除一个数组中的数组元素• PHP数组怎么根据指定条件求和• php怎么去掉数组的部分值• php一维数组怎么去掉一个值• php数组中怎么增加一个值
    1/1

    PHP中文网