Laravel 缓存类原子锁
在后端开发中,关于锁的问题,也是经常能遇到的。为了防止程序并行所导致的数据破坏,或者系统故障,在开发中合理的运用锁,可以带来更好的效果。
Cache::lock
用法:
if (Cache::lock('foo', 10)->get()) {
// 锁住这个key 10秒的时间
// 然后我们可以接着自己实际项目中的相关操作
Cache::lock('foo')->release();//一定要在结尾处释放锁,不然会导致别的并行或后续请求一直无法获取锁而停滞
}
例如最常见我们在处理用户余额的时候需要用到锁
utils工具中封装方法:
/**
* 获取一个原子锁
* @param string $name
* @param int $seconds
* @return Lock
*/
public static function acquireLock(string $name, int $seconds = 60): Lock
{
return Cache::lock($name, $seconds);
}
/**
* 释放一个原子锁
* @param Lock $lock
* @return void
*/
public static function releaseLock(Lock $lock): void
{
$released = optional($lock)->release();
if (!$released) {
optional($lock)->forceRelease();
}
}
调用:
/**
* 增加用户余额
* @param int $userId
* @param int $amountInCents
* @param int $changeType
* @param string $description
* @return UserAccountBalance|null
*/
public function increaseUserBalance(
int $userId,
int $amountInCents,
int $changeType,
string $description = '',
): ?UserAccountBalance {
$lock = Utils::acquireLock('user_balance_' . $userId);
try {
$lock->block(10);
$balance = $this->getBalance($userId);
if (empty($balance)) {
$balance = $this->createBalance([
'user_id' => $userId,
'total_balance_in_cents' => 0,
'withdrawn_balance_in_cents' => 0,
'remaining_balance_in_cents' => 0,
]);
}
if (empty($balance)) {
throw new Exception('创建用户余额失败');
}
if ($amountInCents == 0) {
return $balance;
}
$change = $this->createBalanceChange([
'user_id' => $userId,
'changed_amount_in_cents' => $amountInCents,
'change_type' => $changeType,
'change_detail' => ['description' => $description],
]);
if (empty($change)) {
throw new Exception('余额变更记录保存失败');
}
$this->updateBalance($balance, [
'total_balance_in_cents' => $balance->total_balance_in_cents + $amountInCents,
'remaining_balance_in_cents' => $balance->remaining_balance_in_cents + $amountInCents,
]);
return $balance;
} catch (Exception $error) {
Log::error('用户余额更新失败', [
'userId' => $userId,
'amount_in_cents' => $amountInCents,
'code' => $error->getCode(),
'message' => $error->getMessage(),
'file' => $error->getFile(),
'line' => $error->getLine(),
]);
return null;
} finally {
Utils::releaseLock($lock);
}
}
还没有评论,快来发表第一个评论吧