swoft 使用redis lua script实现高性能抢红包

2 篇文章 0 订阅

废话不多说,直接上代码:

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();
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值