laravel框架中Cache缓存类中的原子锁Cache::lock()解决资金安全问题

2023-06-01 00:00:00 缓存 原子 类中

在后端开发中,关于锁的问题,也是经常能遇到的。为了防止程序并行所导致的数据破坏,或者系统故障,在开发中合理的运用锁,可以带来更好的效果。


而laravel的原子锁则是为我们进行分布式开发时提供了很好的解决方案。

原子锁允许你对分布式锁进行操作而不必担心竞争条件,

你可以通过 Cache::lock 方法来创建和管理锁:

if (Cache::lock('foo', 10)->get()) {
    // 锁住这个key 10秒的时间
    // 然后我们可以接着自己实际项目中的相关操作
    Cache::lock('foo')->release();//一定要在结尾处释放锁,不然会导致别的并行或后续请求一直无法获取锁而停滞
}

ps:

要使用该特性,你的应用必须使用 memcached, dynamodb 或 redis 缓存驱动作为你应用的默认缓存驱动。此外,所有服务器必须与同一中央缓存服务器进行通信。


手册:

https://learnku.com/docs/laravel/5.8/cache/3915#8a1c7c

原子锁Cache::lock()解决资金安全问题测试demo:

环境:

centos + redis + laravel


测试demo代码:

// 出账钱包扣款
$out_purse = null;
$out_lock = Cache::lock('EBank@_transfer:' . $out_purse_id);
try {
   $out_purse = $out_lock->block(50, function () use ($out_purse_id, $amount) {
   $var = FundPurse::where(['id' => $out_purse_id, 'status' => 1])->where('balance', '>=', $amount)->decrement('balance', $amount);
   // 未修改返回修改行数为0
   if (!$var) {
      return false;
    }
       return FundPurse::find($out_purse_id);
    });
       optional($out_lock)->release();
             
    } catch (LockTimeoutException $e) {
   
    } finally {
       optional($out_lock)->release();
    }
   
    if ($out_purse === null) {
       abort(422, '转出钱包查询超时');
    }
   
    if ($out_purse === false) {
       abort(422, '转出钱包扣款失败,余额不足或账户被禁用');
    }


这段代码是所有加减款都必须经过这个方法,所以只在加入这个 lock,

下面的代码类似于乐观锁,利用缓存键加入一个钱包 ID,

只会在这个钱包 ID 业务下 lock,其它钱包 ID 是另一个 lock 键锁互不影响


if 判断不那么优雅,按照文档说法应该放到 catch 里,但是这样效果一样


相关文章