分布式锁获取token

该代码段展示了获取和刷新HS.NET开放API令牌的过程。首先尝试从Redis缓存中获取,若不存在则使用分布式锁进行保护,通过HTTP调用来获取新的令牌,并将其存储到缓存中,确保在令牌过期前更新。同时,提供了刷新令牌的函数,同样使用分布式锁保证并发安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    /**
     * 获取hs.net-openApiToken
     * @param
     * @return java.lang.String
     * @author 德先生
     * @Date 13:21 2021/5/24
     */
    public static String getAuthToken() {
        //首先从redis中获取
        String authToken = LightDataConstant.HS_CACHE.get(LightDataConstant.QUANTIZATION_OPEN_API_AUTH_TOKEN_LIGHT_DATA_ALIAS, String.class);

        //为空 http获取
        if (StringUtils.isBlank(authToken)) {

            //分布式锁:redis为空先上锁 配置文件默认生成锁的时间为30S
            DistributedLock lock = DistributedLockFactory.buildLock(LightDataConstant.LOCK_QUAANTIZATION_FACTOR_AUTH_TOCKEN);
            //获取到锁立即进入,获取不到锁10秒等待
            Long waitSeconds = 10L;
            if (lock.tryLock(waitSeconds, TimeUnit.SECONDS)) {
                //进入锁后再判断一次redis是否有token
                authToken = LightDataConstant.HS_CACHE.get(LightDataConstant.QUANTIZATION_OPEN_API_AUTH_TOKEN_LIGHT_DATA_ALIAS, String.class);
                //再次没有,说明上次HTTP获取Token失败 尝试再次HTTP获取
                if (StringUtils.isBlank(authToken)) {
                    //http获取Token
                    String authorization = "Basic" + " " + Base64.getEncoder().encodeToString((staticKey + ":" + staticSecret).getBytes(StandardCharsets.UTF_8));
                    AuthToken hsNetAuthToken = null;
                    try {
                        //form表单的形式提交
                        List<NameValuePair> nvps = new ArrayList<>();
                        nvps.add(new BasicNameValuePair("grant_type", "client_credentials"));
                        String queryStr = URLEncodedUtils.format(nvps, Consts.UTF_8);
                        ResponseEntity<AuthToken> responseEntity = staticHsNetClient.getAuthToken(authorization, queryStr);
                        hsNetAuthToken = responseEntity.getBody();
                    } catch (Exception e) {
                        log.error("调用hs.net-openApi接口:oauth2/oauth2/token,获取Token失败");
                        log.error(e.getMessage());
                        throw new BaseBizException(ResponseEnum.ERROR_SYSTEM.getErrorCode(), ResponseEnum.ERROR_SYSTEM.getErrorInfo());
                    }
                    //将结果放到缓存 缓存时间比它的失效时间小1800秒
                    assert hsNetAuthToken != null;
                    authToken =  hsNetAuthToken.getAccessToken();
                    LightDataConstant.HS_CACHE.put(LightDataConstant.QUANTIZATION_OPEN_API_AUTH_TOKEN_LIGHT_DATA_ALIAS, authToken, hsNetAuthToken.getExpiresIn() - 1800);
                }

                //最后释放锁
                try {
                    lock.unlock();
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            } else {
                //等待超过10s提示系统繁忙
                log.info("HsNetOpenApiUtil.getAuthToken()等待超过10秒,未获取到锁,未进入HTTP请求");
                throw new BaseBizException(ResponseEnum.ERROR_SYSTEM.getErrorCode(), ResponseEnum.ERROR_SYSTEM.getErrorInfo());
            }
        }
        return authToken;
    }

    public static String freshAuthToken() {
        String authToken;
        //分布式锁:redis为空先上锁 配置文件默认生成锁的时间为30S
        DistributedLock lock = DistributedLockFactory.buildLock(LightDataConstant.LOCK_QUAANTIZATION_FACTOR_AUTH_TOCKEN);
        //获取到锁立即进入,获取不到锁立即拒绝
        if (lock.tryLock()) {
            //http获取Token
            String authorization = "Basic" + " " + Base64.getEncoder().encodeToString((staticKey + ":" + staticSecret).getBytes(StandardCharsets.UTF_8));
            AuthToken hsNetAuthToken = null;
            try {
                //form表单的形式提交
                List<NameValuePair> nvps = new ArrayList<>();
                nvps.add(new BasicNameValuePair("grant_type", "client_credentials"));
                String queryStr = URLEncodedUtils.format(nvps, Consts.UTF_8);
                ResponseEntity<AuthToken> responseEntity = staticHsNetClient.getAuthToken(authorization, queryStr);
                hsNetAuthToken = responseEntity.getBody();
            } catch (Exception e) {
                log.error("调用hs.net-openApi接口:oauth2/oauth2/token,获取Token失败");
                log.error(e.getMessage());
                throw new BaseBizException(ResponseEnum.ERROR_SYSTEM.getErrorCode(), ResponseEnum.ERROR_SYSTEM.getErrorInfo());
            }
            //将结果放到缓存 缓存时间比它的失效时间小1800秒
            assert hsNetAuthToken != null;
            authToken = hsNetAuthToken.getAccessToken();
            LightDataConstant.HS_CACHE.put(LightDataConstant.QUANTIZATION_OPEN_API_AUTH_TOKEN_LIGHT_DATA_ALIAS, authToken, hsNetAuthToken.getExpiresIn() - 1800);

            //最后释放锁
            try {
                lock.unlock();
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        } else {
            //等待超过10s提示系统繁忙
            log.info("HsNetOpenApiUtil.freshAuthToken()未获取到锁,立即拒绝,未进入HTTP请求");
            throw new BaseBizException(ResponseEnum.ERROR_SYSTEM.getErrorCode(), ResponseEnum.ERROR_SYSTEM.getErrorInfo());
        }
        return authToken;
    }
### Redisson与Zookeeper分布式锁机制对比 #### Redisson分布式锁实现 Redisson提供了多种类型的分布式锁,其中最常用的是基于Redis的单节点锁定算法。这种锁通过条件设置(如果不存在则设置)来获取锁,并通过原子删除(仅当值匹配时才删除)释放锁[^1]。 ```java RLock lock = redisson.getLock("anyLock"); lock.lock(); try { // 执行业务逻辑 } finally { lock.unlock(); } ``` 这种方式简单易懂,在大多数情况下能够满足需求。然而需要注意的是,这些锁只是尽力而为的最佳实践,并不完全可靠,可能会偶尔失败。因此建议在代码中清晰记录这一点并考虑其适用场景。 #### Zookeeper分布式锁实现 相比之下,Zookeeper实现了更为严格的分布式协调服务。它利用`znode`版本号作为围栏令牌(fencing token),确保每次写入操作都携带一个严格单调递增的令牌。这使得即使在网络分区或其他异常条件下也能保持数据一致性[^2]。 创建临时顺序节点以构建分布式互斥锁: ```java InterProcessMutex mutex = new InterProcessMutex(client, "/examples/locks"); mutex.acquire(); try { // 执行业务逻辑 } finally { mutex.release(); } ``` 这里的关键在于存储服务器会主动验证令牌的有效性,拒绝任何使令牌回退的操作请求。只要锁服务能生成严格单调增加的令牌,则此方法就是安全可靠的。 #### 两者之间的差异 - **可靠性**: Zookeeper由于采用了更复杂的协议设计以及更强的一致性保障措施,在面对复杂网络环境下的表现更加稳定;而Redisson虽然也具备一定的容错能力,但在极端情况下可能无法保证绝对的安全性。 - **性能开销**: 单纯从效率角度来看,采用简单的SETNX(SET If Not eXists)指令实现加解锁过程显然要比维护整个集群状态要轻量得多。所以对于那些不需要极高一致性的应用场景来说,使用Redisson可能是更好的选择。 - **配置难度**: 设置和管理一个多副本组成的Zookeeper群集相对较为繁琐一些,涉及到更多参数调整和技术细节处理;相反地,只需要连接到一台Redis实例就可以轻松部署起一套基本可用的Redisson方案来了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值