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

本文环境 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 实例

//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位图命令

# 用户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服务

<?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 Illuminate\Redis\RedisManager;
use Workerman\Timer;

/**
 * Class Redis
 *
 * @package support
 *
 * Strings methods
 * @method static int append($key, $value)
 * @method static int bitCount($key)
 * @method static int bitPos($key, $value, $start, $end)
 * @method static int decr($key, $value = 1)
 * @method static int decrBy($key, $value)
 * @method static string|bool get($key)
 * @method static int getBit($key, $offset)
 * @method static string getRange($key, $start, $end)
 * @method static string getSet($key, $value)
 * @method static int incr($key, $value = 1)
 * @method static int incrBy($key, $value)
 * @method static float incrByFloat($key, $value)
 * @method static array mGet(array $keys)
 * @method static array getMultiple(array $keys)
 * @method static bool mSet($pairs)
 * @method static bool mSetNx($pairs)
 * @method static bool set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
 * @method static bool setBit($key, $offset, $value)
 * @method static bool setEx($key, $ttl, $value)
 * @method static bool pSetEx($key, $ttl, $value)
 * @method static bool setNx($key, $value)
 * @method static string setRange($key, $offset, $value)
 * @method static int strLen($key)
 * Keys methods
 * @method static int del(...$keys)
 * @method static int unlink(...$keys)
 * @method static false|string dump($key)
 * @method static int exists(...$keys)
 * @method static bool expire($key, $ttl)
 * @method static bool pexpire($key, $ttl)
 * @method static bool expireAt($key, $timestamp)
 * @method static bool pexpireAt($key, $timestamp)
 * @method static array keys($pattern)
 * @method static bool|array scan($it)
 * @method static void migrate($host, $port, $keys, $dbIndex, $timeout, $copy = false, $replace = false)
 * @method static bool select($dbIndex)
 * @method static bool move($key, $dbIndex)
 * @method static string|int|bool object($information, $key)
 * @method static bool persist($key)
 * @method static string randomKey()
 * @method static bool rename($srcKey, $dstKey)
 * @method static bool renameNx($srcKey, $dstKey)
 * @method static string type($key)
 * @method static int|array sort($key, $options = [])
 * @method static int ttl($key)
 * @method static int pttl($key)
 * @method static void restore($key, $ttl, $value)
 * Hashes methods
 * @method static false|int hSet($key, $hashKey, $value)
 * @method static bool hSetNx($key, $hashKey, $value)
 * @method static false|string hGet($key, $hashKey)
 * @method static false|int hLen($key)
 * @method static false|int hDel($key, ...$hashKeys)
 * @method static array hKeys($key)
 * @method static array hVals($key)
 * @method static array hGetAll($key)
 * @method static bool hExists($key, $hashKey)
 * @method static int hIncrBy($key, $hashKey, $value)
 * @method static float hIncrByFloat($key, $hashKey, $value)
 * @method static bool hMSet($key, $members)
 * @method static array hMGet($key, $memberKeys)
 * @method static array hScan($key, $iterator, $pattern = '', $count = 0)
 * @method static int hStrLen($key, $hashKey)
 * Lists methods
 * @method static array blPop($keys, $timeout)
 * @method static array brPop($keys, $timeout)
 * @method static false|string bRPopLPush($srcKey, $dstKey, $timeout)
 * @method static false|string lIndex($key, $index)
 * @method static int lInsert($key, $position, $pivot, $value)
 * @method static false|string lPop($key)
 * @method static false|int lPush($key, ...$entries)
 * @method static false|int lPushx($key, $value)
 * @method static array lRange($key, $start, $end)
 * @method static false|int lRem($key, $count, $value)
 * @method static bool lSet($key, $index, $value)
 * @method static false|array lTrim($key, $start, $end)
 * @method static false|string rPop($key)
 * @method static false|string rPopLPush($srcKey, $dstKey)
 * @method static false|int rPush($key, ...$entries)
 * @method static false|int rPushX($key, $value)
 * @method static false|int lLen($key)
 * Sets methods
 * @method static int sAdd($key, $value)
 * @method static int sCard($key)
 * @method static array sDiff($keys)
 * @method static false|int sDiffStore($dst, $keys)
 * @method static false|array sInter($keys)
 * @method static false|int sInterStore($dst, $keys)
 * @method static bool sIsMember($key, $member)
 * @method static array sMembers($key)
 * @method static bool sMove($src, $dst, $member)
 * @method static false|string|array sPop($key, $count = 0)
 * @method static false|string|array sRandMember($key, $count = 0)
 * @method static int sRem($key, ...$members)
 * @method static array sUnion(...$keys)
 * @method static false|int sUnionStore($dst, ...$keys)
 * @method static false|array sScan($key, $iterator, $pattern = '', $count = 0)
 * Sorted sets methods
 * @method static array bzPopMin($keys, $timeout)
 * @method static array bzPopMax($keys, $timeout)
 * @method static int zAdd($key, $score, $value)
 * @method static int zCard($key)
 * @method static int zCount($key, $start, $end)
 * @method static double zIncrBy($key, $value, $member)
 * @method static int zinterstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '')
 * @method static array zPopMin($key, $count)
 * @method static array zPopMax($key, $count)
 * @method static array zRange($key, $start, $end, $withScores = false)
 * @method static array zRangeByScore($key, $start, $end, $options = [])
 * @method static array zRevRangeByScore($key, $start, $end, $options = [])
 * @method static array zRangeByLex($key, $min, $max, $offset = 0, $limit = 0)
 * @method static int zRank($key, $member)
 * @method static int zRevRank($key, $member)
 * @method static int zRem($key, ...$members)
 * @method static int zRemRangeByRank($key, $start, $end)
 * @method static int zRemRangeByScore($key, $start, $end)
 * @method static array zRevRange($key, $start, $end, $withScores = false)
 * @method static double zScore($key, $member)
 * @method static int zunionstore($keyOutput, $arrayZSetKeys, $arrayWeights = [], $aggregateFunction = '')
 * @method static false|array zScan($key, $iterator, $pattern = '', $count = 0)
 * HyperLogLogs methods
 * @method static int pfAdd($key, $values)
 * @method static int pfCount($keys)
 * @method static bool pfMerge($dstKey, $srcKeys)
 * Geocoding methods
 * @method static int geoAdd($key, $longitude, $latitude, $member, ...$items)
 * @method static array geoHash($key, ...$members)
 * @method static array geoPos($key, ...$members)
 * @method static double geoDist($key, $members, $unit = '')
 * @method static int|array geoRadius($key, $longitude, $latitude, $radius, $unit, $options = [])
 * @method static array geoRadiusByMember($key, $member, $radius, $units, $options = [])
 * Streams methods
 * @method static int xAck($stream, $group, $arrMessages)
 * @method static string xAdd($strKey, $strId, $arrMessage, $iMaxLen = 0, $booApproximate = false)
 * @method static array xClaim($strKey, $strGroup, $strConsumer, $minIdleTime, $arrIds, $arrOptions = [])
 * @method static int xDel($strKey, $arrIds)
 * @method static mixed xGroup($command, $strKey, $strGroup, $strMsgId, $booMKStream = null)
 * @method static mixed xInfo($command, $strStream, $strGroup = null)
 * @method static int xLen($stream)
 * @method static array xPending($strStream, $strGroup, $strStart = 0, $strEnd = 0, $iCount = 0, $strConsumer = null)
 * @method static array xRange($strStream, $strStart, $strEnd, $iCount = 0)
 * @method static array xRead($arrStreams, $iCount = 0, $iBlock = null)
 * @method static array xReadGroup($strGroup, $strConsumer, $arrStreams, $iCount = 0, $iBlock = null)
 * @method static array xRevRange($strStream, $strEnd, $strStart, $iCount = 0)
 * @method static int xTrim($strStream, $iMaxLen, $booApproximate = null)
 * Pub/sub methods
 * @method static mixed pSubscribe($patterns, $callback)
 * @method static mixed publish($channel, $message)
 * @method static mixed subscribe($channels, $callback)
 * @method static mixed pubSub($keyword, $argument = null)
 * Generic methods
 * @method static mixed rawCommand(...$commandAndArgs)
 * Transactions methods
 * @method static \Redis multi()
 * @method static mixed exec()
 * @method static mixed discard()
 * @method static mixed watch($keys)
 * @method static mixed unwatch($keys)
 * Scripting methods
 * @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
 * @method static mixed evalSha($scriptSha, $numkeys, ...$arguments)
 * @method static mixed script($command, ...$scripts)
 * @method static mixed client(...$args)
 * @method static null|string getLastError()
 * @method static bool clearLastError()
 * @method static mixed _prefix($value)
 * @method static mixed _serialize($value)
 * @method static mixed _unserialize($value)
 * Introspection methods
 * @method static bool isConnected()
 * @method static mixed getHost()
 * @method static mixed getPort()
 * @method static false|int getDbNum()
 * @method static false|double getTimeout()
 * @method static mixed getReadTimeout()
 * @method static mixed getPersistentID()
 * @method static mixed getAuth()
 */
class Redis
{

    /**
     * @var RedisManager
     */
    protected static $_instance = null;

    /**
     * @return RedisManager
     */
    public static function instance()
    {
        if (!static::$_instance) {
            $config            = config('redis');
            static::$_instance = new RedisManager('', 'phpredis', $config);
        }
        return static::$_instance;
    }

    /**
     * @param string $name
     * @return \Illuminate\Redis\Connections\Connection
     */
    public static function connection($name = 'default')
    {
//        return static::instance()->connection($name);
        static $timers = [];
        $connection = static::instance()->connection($name);
        if (!isset($timers[$name])) {
            $timers[$name] = Timer::add(55, function () use ($connection) {
                $connection->get('ping');
            });
        }
        return $connection;
    }

    /**
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public static function __callStatic($name, $arguments)
    {
        return static::instance()->connection('default')->{$name}(... $arguments);
    }
}

Buy me a cup of coffee :)

觉得对你有帮助,就给我打赏吧,谢谢!

Buy me a cup of coffee :)

https://www.owenzhang.com/wechat_reward.png

合智互联客户成功服务热线:400-1565-661

admin
admin管理员

上一篇:揭秘: 百万销冠门店只因做对这件事
下一篇:Windows搭建 我的世界(mc) 1.18.2 服务器教程,Minecraft开服教程

留言评论

暂无留言