PHP Redis基于TP5.1框架实现互斥锁

23 篇文章 0 订阅

互斥锁常见使用场景: 缓存穿透

看下面这两个用户其实都请求了数据库,假设用户量大,会导致大量请求打到数据库导致宕机

 用户A           查询缓存       没有缓存      请求数据库      添加缓存并设置过期时间

 用户B                查询缓存  没有缓存 请求数据库                添加缓存并设置过期时间

 

我的这个demo是苹果授权的服务层代码 , 这里获取的数据是可以重复使用的,但是所有用户都请求会浪费服务器资源,所以添加一个互斥锁减少服务器的消耗

<?php

/**
 * 苹果验证类
 * Date: 2019/9/11
 */

namespace app\api\controller\v1;

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use think\facade\Cache;

class AppleInterface
{

    /**
     * @Description:redis加锁
     * @param $key
     * @param $random
     * @return void
     * @author: Msy
     * @Created-Time: 2022/7/11 15:21
     */
    public function lock($key ,$random)
    {
        $redis = Cache::store('redis')->handler();
//        return Cache::store('redis')->set($key ,$random,["NX","EX"=>60]); // tp不支持,只能原生写, 原生key有问题,有配置项前缀的
        $key = config("cache.redis.prefix").$key;
        return $redis->set($key ,$random,["NX","EX"=>60]);
    }

    /**
     * @Description:释放redis锁
     * @param $key
     * @return void
     * @author: Msy
     * @Created-Time: 2022/7/11 15:21
     */
    public function unlock($key,$random)
    {
        $lock_value=Cache::store("redis")->get($key);

        if ($lock_value == $random){
            Cache::store("redis")->rm($key);
        }
    }

    /**
     * @Description:
     * @return void
     * @author: Msy
     * @Created-Time: 2022/7/11 15:20
     */
    public function getJWKSInCache()
    {
        $key = ':apple:JWKS';
        $ret = Cache::store("redis")->get($key);
        if (!$ret) {
            $lockKey = $key . '_lock';
            $random = mt_rand();
            //拿到互斥锁
            if ($this->lock($lockKey, $random)) {
                $value = file_get_contents('https://appleid.apple.com/auth/keys');
                Cache::store("redis")->set($key, $value, 86400);
                //释放锁
                $this->unlock($lockKey, $random);
                return $value;
            } else {
                //等待200毫秒,然后重新获取缓存值,让其他获取到锁的进程取得数据并设置缓存
                usleep(200);
                getJWKSInCache();
            }
        } else {
            return $ret;
        }
    }

    /**
     * 验证token是否正常
     * 验证准确性:通过Apple公钥在线(https://8gwifi.org/jwkconvertfunctions.jsp)得到用于解密的pem公钥字符串
     * @param string $identityToken 前端获取的token
     * @return bool|object
     * @throws \Firebase\JWT\InvalidArgumentException
     */
    public function apple_jwt_verify($identityToken = '' ,$i=0)
    {
        if($i>1) return false;

        // 调用苹果接口获取JWKS
//        $apiResponse = file_get_contents('https://appleid.apple.com/auth/keys');
        $apiResponse = $this->getJWKSInCache();


        $jwkSet = json_decode($apiResponse, true);
        if (!is_array($jwkSet)) {
            exception("Get JWK failed");
        }

        // 将JWKS转换为公钥
        try {
            $pubKeys = JWk::parseKeySet($jwkSet);
        } catch (\Exception $e) {
            Cache::store("redis")->rm(':apple:JWKS');
            $i++;
            $this->apple_jwt_verify($identityToken,$i);
            exception("Parse key set failed:" . $e->getMessage());
        }

        // 使用公钥对JWT进行检验
        try {
            $payload = JWT::decode($identityToken, $pubKeys, ['RS256']);
        } catch (\Exception $e) {
            exception("Decode JWT failed:" . $e->getMessage());
        }

        // 若检验通过,会得到JWT里的payload信息
        return json_decode((json_encode($payload)), true);
    }

}

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苗先生的PHP记录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值