laravel框架中Cache缓存类中的原子锁Cache::lock()解决资金安全问题
在后端开发中,关于锁的问题,也是经常能遇到的。为了防止程序并行所导致的数据破坏,或者系统故障,在开发中合理的运用锁,可以带来更好的效果。
而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 里,但是这样效果一样
相关文章