玩具:Swoole的Redis服务端中重新定义Hash

背景

Redis中的Hash类型无法设置其中某个键具有过期时间,只能额外记录对应的过期时间,在业务代码中判断是否过期。学习Swoole的过程中看到可以自定义相关命令,于是动手做了该功能,并集成到了Laravel中。

代码

<?php
use Swoole\Redis\Server;

define('DB_FILE', __DIR__ . '/db');
//新Hash参数个数
define('EHASH_PARAM_SIZE', 3);


$server = new Server("0.0.0.0", 9501, SWOOLE_BASE);

if (is_file(DB_FILE)) {
    $server->data = unserialize(file_get_contents(DB_FILE));
} else {
    $server->data = [];
}

$server->setHandler('select', function ($fd, $data) use ($server){
    //只有一个db,不用select
    return $server->send($fd, Server::format(Server::STATUS, 'OK'));
});
//命令格式hset key field value expiration
$server->setHandler('hset', function ($fd, $data) use ($server) {
    if (count($data) < EHASH_PARAM_SIZE) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hset' command"));
    }

    $key = $data[0];
    $name = $data[1];
    $value = $data[2];
    $expire = $data[3] ?? 0;
    //field未做hash处理,效率高一点,也不会出现hash冲突
    $server->data[$key][$name] = ['value'=>$value,'expire'=>$expire ? (time() + $expire) : 0];
    return $server->send($fd, Server::format(Server::INT, 1));
});

$server->setHandler('hmset', function ($fd, $data) use ($server) {
    $size = count($data) - 1;
    if ($size < EHASH_PARAM_SIZE || $size % EHASH_PARAM_SIZE) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hmset' command"));
    }
    $key = $data[0];
    unset($data[0]);
    $data_chunk = array_chunk($data, EHASH_PARAM_SIZE);
    $timestamp = time();
    foreach ($data_chunk as $chunk) {
        if (!is_numeric($chunk[EHASH_PARAM_SIZE - 1])) {
            return $server->send($fd, Server::format(Server::ERROR, "ERR wrong type of arguments for 'hmset' command"));
        }
        $server->data[$key][$chunk[0]] = ['value'=>$chunk[1],'expire'=>$chunk[2] ? ($timestamp + intval($chunk[2])) : 0];
    }
    return $server->send($fd, Server::format(Server::STATUS, 'OK'));
});

$server->setHandler('hget', function ($fd, $data) use ($server) {
    if (count($data) != 2) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hget' command"));
    }
    $key = $data[0];
    $name = $data[1];
    if (!empty($server->data[$key][$name]) && (!$server->data[$key][$name]['expire'] || $server->data[$key][$name]['expire'] > time())) {
        return $server->send($fd, Server::format(Server::STRING, $server->data[$key][$name]['value']));
    }
    //移除超时数据
    unset($server->data[$key][$name]);
    return $server->send($fd, Server::format(Server::NIL));
});

$server->setHandler('hgetall', function ($fd, $data) use ($server) {
    if (count($data) != 1) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hgetall' command"));
    }
    $key = $data[0];
    if (!isset($server->data[$key])) {
        return $server->send($fd, Server::format(Server::NIL));
    }
    $timestamp = time();
    //移除超时数据
    $server->data[$key] = array_filter($server->data[$key], function ($item) use ($timestamp){
        return !$item['expire'] || $item['expire'] > $timestamp;
    });
    $result = array_map(function ($item) {
        return $item['value'];
    }, $server->data[$key]);
    return $server->send($fd, Server::format(Server::MAP, $result));
});

$server->on('WorkerStart', function ($server) {
    $server->tick(10000, function () use ($server) {
        file_put_contents(DB_FILE, serialize($server->data));
    });
});

$server->start();

上面代码只实现了部分Hash命令,hlen、hdel等命令也不难实现,有兴趣可以自行尝试。
保存在redis.php文件中后,命令启动php redis.php。

终端下连接:

参考文章

blog.csdn.net/zhangyue0503/article...

结尾

下一篇将具体写一下集成到Laravel的过程,以上有错误或需改进之处可以在评论区指出。一起学习,一起进步!

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!