高并发解决方案之 redis 分布式锁

背景:秒杀服务中要写一个定时任务:活动到期时给order微服务发送关闭订单的通知。这需要改变数据库表中的数据,而集群中服务是多节点的方式进行部署,会出现并发执行的情况,所以采用的redis的分布式锁的实现方式。
Redis 锁(setNx)
特点: 如果没有获取到锁,请求会被丢弃。 只适合 消息队列 和定时任务场景

点击查看代码

public function sendEndNotice()
    {
        $lock = new QueueLockLogic(LimitCFG::CLOSE_LOCK_KEY);
        $lock_res = $lock->lock(LimitCFG::CLOSE_LOCK_KEY);
        if (!$lock_res) return false;
        //...
        $res = $lock->unlock(LimitCFG::CLOSE_LOCK_KEY);
        return $res;
    }
	
	
	//再附上一个应用于砍价服务中消息队列的
	加锁:
        $uniqueKey = "bargain:cmq:upOrderStatus:$storeId_$branchId_$orderId_$status"; 
        $lock = new QueueLockLogic($uniqueKey);
        $lockRes = $lock->lock($uniqueKey);
        if (!$lockRes){
            throw new ErrException(ExceCode::E13161);
        }

		.....

	解锁:
	  $res = $lock->unlock($uniqueKey);

	
	===================方法原型============================
namespace App\Models\Logic;

use Swoft\Redis\Redis;
use Swoft\App;


/**
 * Class QueueLockLogic
 * @package App\Models\Logic
 */
class QueueLockLogic
{
	private $redis;

	private $prefix;

	/**
	 *
	 * @param
	 */
	public function __construct(string $redisPrefix)
	{
		$this->redis = App::getBean(Redis::class);
		$this->prefix = $redisPrefix;
	}

	/**
	 * 获取redis键
	 *
	 * @param string $unique
	 * @return string
	 */
	public function getKey(string $unique): string
	{
		return $this->prefix . $unique;
	}

	/**
	 * 获取锁
	 * 返回:
	 *     true: 说明获得锁,
	 *     false: 说明获得锁失败
	 *
	 * @param string  $key
	 * @param integer $expireTime
	 * @return boolean
	 */
	public function lock(string $key, int $ttl = 60): bool
	{
		//加锁
		$res = $this->redis->setNx($key, time() + $ttl);

		if ($res === 0) { //加锁失败
			//检测锁是否过期
			$expireTime = $this->redis->get($key);
			if ($expireTime < time()) { //锁已过期,获取锁
				$oldExpireTime = $this->redis->getSet($key, time() + $ttl);    //在查询期间可能有别的进程加锁,也许已经加锁成功。
				if ($oldExpireTime < time()) {//加锁成功
					return true;
				}//即便加锁失败,重置了key的值也相差无几可忽略
			}

			return false;
		}

		return true;
	}

	/**
	 * 解锁
	 * @param string $key
	 * @return bool
	 */
	public function unlock(string $key): bool
	{
		return $this->redis->delete($key) > 0;
	}

}