workerman实现redis bit位图实现用户签到PHP实例

2022-06-08 04:45:41 浏览数 (1)

本文环境 CentOS8.0,PHP8.1,Nginx1.8,Workerman 4.0 不懂的可以评论或联系我邮箱:owen@owenzhang.com 著作权归OwenZhang所有。商业转载请联系OwenZhang获得授权,非商业转载请注明出处。

image.png

workerman介绍

Workerman是一款纯PHP开发的开源高性能的PHP 应用容器。

Workerman不是重复造轮子,它不是一个MVC框架,而是一个更底层更通用的服务框架,你可以用它开发tcp代理、做游戏服务器、邮件服务器、ftp服务器、甚至开发一个php版本的redis、php版本的数据库、php版本的nginx、php版本的php-fpm等等。Workerman可以说是PHP领域的一次创新,让开发者彻底摆脱了PHP只能做WEB的束缚。

实际上Workerman类似一个PHP版本的nginx,核心也是多进程 Epoll 非阻塞IO。Workerman每个进程能维持上万并发连接。由于本身常驻内存,不依赖Apache、nginx、php-fpm这些容器,拥有超高的性能。同时支持TCP、UDP、UNIXSOCKET,支持长连接,支持Websocket、HTTP、WSS、HTTPS等通讯协议以及各种自定义协议。拥有定时器、异步socket客户端、异步Redis、异步Http、异步消息队列等众多高性能组件。

Redis bitmap位图介绍

在平时开发过程中,经常会有一些 bool 类型数据需要存取。比如记录用户一年内签到的次数,签了是 1,没签是 0。如果使用 key-value 来存储,那么每个用户都要记录 365 次,当用户成百上亿时,需要的存储空间将非常巨大。为了解决这个问题,Redis 提供了位图结构。

位图(bitmap)同样属于 string 数据类型。Redis 中一个字符串类型的值最多能存储 512 MB 的内容,每个字符串由多个字节组成,每个字节又由 8 个 Bit 位组成。位图结构正是使用“位”来实现存储的,它通过将比特位设置为 0 或 1来达到数据存取的目的,这大大增加了 value 存储数量,它存储上限为2^32 。

php 实例

代码语言:javascript复制
//setBit($key, $offset, $value)
        //$offset参数需要大于等于0,并且小于 2^32 = 4294967296 (bitmaps 最大 512MB)
        //$value 偏移量上的位 0和1
        //不建议用下面方法,这样造成0到20220101值为空 数据量太大
        // 可以key上面显示年份或者月份,然后day1-365这样 key大小就比较合理
        $data = [
            [
                'day'     => 20220101,//日期 2022年1月1号
                'sign_in' => 1,//签到 1有 0无
            ],
            [
                'day'     => 20220102,
                'sign_in' => 0,
            ],
            [
                'day'     => 20220322,
                'sign_in' => 1,
            ],
        ];
        //1-1231不存在的值都设置为0
        $data = [
            [
                'day'     => 101,//日期 1月1号
                'sign_in' => 1,//签到 1有 0无
            ],
            [
                'day'     => 211,
                'sign_in' => 0,
            ],
            [
                'day'     => 1102,
                'sign_in' => 1,
            ],
            [
                'day'     => 1231,
                'sign_in' => 1,
            ],
        ];
//        foreach ($data as $datum) {
//            //写入用户10026的签到位图
//            Redis::setBit('bitSingIn:userId:10026', $datum['day'], $datum['sign_in']);
//        }

        $day = $request->post('day');
        //获取用户10026的某天签到情况
        $sign_in = Redis::getBit('bitSingIn:userId:10026', $day);
        //统计用户的签到次数
        $bitCount = Redis::bitCount('bitSingIn:userId:10026');
        //获取首次签到的日期
        $bitCount = Redis::bitPos('bitSingIn:userId:10026', 1);

        return $this->apiSuccess(compact('day', 'sign_in', 'bitCount'));

复制

redis bit位图命令

代码语言:javascript复制
# 用户2月17号签到
SETBIT u:sign:1000:201902 16 1 # 偏移量是从0开始,所以要把17减1

# 检查2月17号是否签到
GETBIT u:sign:1000:201902 16 # 偏移量是从0开始,所以要把17减1

# 统计2月份的签到次数
BITCOUNT u:sign:1000:201902

# 获取2月份前28天的签到数据
BITFIELD u:sign:1000:201902 get u28 0

# 获取2月份首次签到的日期
BITPOS u:sign:1000:201902 1 # 返回的首次签到的偏移量,加上1即为当月的某一天

复制

场景需求

适用场景如签到送积分、签到领取奖励等,大致需求如下:

签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等。

如果连续签到中断,则重置计数,每月初重置计数。

当月签到满3天领取奖励1,满5天领取奖励2,满7天领取奖励3……等等。

显示用户某个月的签到次数和首次签到时间。

在日历控件上展示用户每月签到情况,可以切换年月显示……等等。

运行结果和redis key存储情况

redis服务

代码语言:javascript复制
<?php
/**
 * This file is part of webman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 */

namespace support;

use IlluminateRedisRedisManager;
use WorkermanTimer;

0 人点赞