玩具: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 协议》,转载必须注明作者和本文链接