废话不多说,直接上代码:
if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then
return 1;--该用户已抢过红包
else
-- 先取出一个小红包
local hongBao = redis.call('rpop', KEYS[1]);
if hongBao then
local x = cjson.decode(hongBao);
-- 加入用户ID信息
x['userId'] = KEYS[4];
local re = cjson.encode(x);
-- 把用户ID放到去重的set里
redis.call('hset', KEYS[3], KEYS[4], KEYS[4]);
-- 把红包放到已消费队列里
redis.call('lpush', KEYS[2], re);
return 2;--抢红包成功
end
return 3;--红包已抢完
end
1.在app目录下新建Lua文件夹,上面代码保存为hongbao.lua
2.config/define.php增加'@lua' => '@app/Lua'
$aliases = [
'@root' => BASE_PATH,
'@env' => '@root',
'@app' => '@root/app',
'@res' => '@root/resources',
'@runtime' => '@root/runtime',
'@configs' => '@root/config',
'@resources' => '@root/resources',
'@beans' => '@configs/beans',
'@properties' => '@configs/properties',
'@console' => '@beans/console.php',
'@commands' => '@app/command',
'@vendor' => '@root/vendor',
'@lua' => '@app/Lua',
];
3.App/Boot 服务启动时开启Server前置进程,加载好lua脚本
namespace App\Boot;
use App\Util\LuaTable;
use Swoft\App;
use Swoft\Memory\Table;
use Swoft\Process\ProcessInterface;
use Swoft\Core\Coroutine;
use Swoft\Process\Bean\Annotation\Process;
use Swoft\Process\Process as SwoftProcess;
/**
* Custom process
*
* @Process(name="luaLoadProcess", boot=true, coroutine=true)
*/
class LuaLoadProcess implements ProcessInterface
{
private $luaTable;
public function run(SwoftProcess $process)
{
$filePath = App::getAlias('@lua');
$files = $this->getFilesByExt($filePath, 'lua');
$sha1s = [];
$luas = [];
$names = [];
foreach ($files as $file) {
$lua = file_get_contents($filePath.'/'.$file);//将整个文件内容读入到一个字符串中
$lua = str_replace("\r\n", " ", $lua);
$lua = str_replace("\n", " ", $lua);
list($name, $ext) = explode('.', $file);
$sha1s[] = sha1($lua);
$luas[] = $lua;
$names[] = $name;
}
$this->registerLuas($sha1s, $luas, $names);
//ProcessBuilder::create('customProcess')->start();
}
private function getFilesByExt($path, $ext)
{
$files = array();
if (is_dir($path)) {
$handle = opendir($path);
while ($file = readdir($handle)) {
if ($file[0] == '.') {
continue;
}
if (is_file($path .'/'. $file) and preg_match('/\.' . $ext . '$/', $file)) {
$files[] = $file;
}
}
closedir($handle);
}
return $files;
}
private function registerLuas($sha1s, $luas, $names)
{
$redis = App::getBean(\Swoft\Redis\Redis::class);
$exists = $redis->script("exists", ...$sha1s);
if ($exists == false) {
App::error('该RedisServer不支持lua脚本。');
return false;
}
$count = count($exists);
for ($i = 0; $i < $count; $i++) {
if (!$exists[$i]) {
$redis->script("load", $luas[$i]);
}
//记录到Table
LuaTable::init();
LuaTable::getInstance()->getLuaTable()->set(md5($names[$i]),['fileName' => $names[$i], 'sha1' => $sha1s[$i]]);
App::info("已加载$names[$i]脚本");
}
return true;
}
public function check(): bool
{
return true;
}
}
4.领取红包
$args = ['test_hongBaoList-'.$activeId, 'test_hongBaoConsumedList-'.$activeId, 'test_hongBaoConsumedMap-'.$activeId, $uid.''];
try {
LuaTable::init();
sha1 = LuaTable::getInstance()->getLuaTable()->get(md5('hongbao'));
$result = $this->redis->evalSha($sha1, $args, 4);
} catch (\Exception $e) {
return $e->getMessage();
}