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


点赞1
点击评论0
收藏0
浏览 21
 

还没有评论,快来发表第一个评论吧

免责声明:凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证有关资料的准确性及可靠性,注册用户和一般页面游览者在使用前请进一步核实,并对任何自主决定的行为负责。本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任(包括侵权责任、合同责任和其它责任)
*尊重作者,转载请注明出处!