PHP进阶与redis锁限制并发访问功能示例

2022-09-11 10:26:14 浏览数 (2)

框架Yii2 1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。

例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。 伪代码如下:

代码语言:javascript复制
if A(可以换领) 
   B(执行换领) 
   C(更新为已换领) 
   D(结束) 

如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。

因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。?

代码语言:javascript复制
<?php
/**
 * Created by ZhengNiu.
 * User: 77103
 * Date: 2019/3/28
 * Time: 9:38
 */

namespace apphelper;

use Yii;

class RedisLock
{
    private $_redis;
    private $key;
    private $expire;

    /**
     * 初始化redis
     *
     * RedisLock constructor.
     * @param string $key 锁标识
     * @param int $expire 锁过期时间
     */
    public function __construct($key, $expire = 5)
    {
        try{
            $this->_redis = Yii::$app->redis;
            $this->key = $key;
            $this->expire = $expire;
        }catch (Exception $ex) {
            return  new Exception($ex->getMessage());
        }
    }

    /**
     * 获取锁
     *
     * @return bool
     */
    public function lock()
    {
        //不存在则返回1,存在返回0
        $is_lock = $this->_redis->setnx($this->key, time()   $this->expire);
        // 不能获取锁
        if (!$is_lock) {

            // 判断锁是否过期
            $lock_time = $this->_redis->get($this->key);

            // 锁已过期,删除锁,重新获取
            if (time() > $lock_time) {
                $this->unlock($this->key);
                $is_lock = $this->_redis->setnx($this->key, time()   $this->expire);
            }
        }

        return $is_lock ? true : false;
    }

    /**
     * 释放锁
     *
     * @return Boolean
     */

    public function unlock()
    {
        return $this->_redis->del($this->key);
    }
}

调用:

代码语言:javascript复制
<?php

    /**
     * 锁机制
     */
    public function actionLock()
    {
        $this->layout = false;
        $oRedisLock = new RedisLock('test2', 10);// 创建redislock对象
        $is_lock = $oRedisLock->lock();
        if ($is_lock) {
            echo 'get lock success</br>';
            //业务逻辑start
            sleep(5);
            //业务逻辑end
            $oRedisLock->unlock();
        } else {
            echo 'request too frequently</br>';
        }
    }

测试结果:打开两个浏览器 先访问的浏览器

代码语言:javascript复制
get lock success
do sth..
success

后者:

代码语言:javascript复制
request too frequently

0 人点赞