怎么使用zookeeper限制同一个请求

目录

生产中,我们经常会把接口api 提供给第三方的使用者,但是 如果当调用者出现故障疯狂的调用我们的接口,或者 同一个请求发了多次,第一种 情况 会造成 服务器大量的链接被占用,造成服务挂掉,正常的服务无法提供给其他人, 第二种情况会造成 业务上很大的困扰,比如 减库存或者转账多加多减得情况,

解决调用频率太高的问题

我使用的是 redis 作为解决调用频率的问题
首先 我们将 需要 进行 访问频率的url列出来,给他们限制住 访问的间隔时间,然后在 拦截器中 使用redis 去判断每一次进来的请求 是否 符合 间隔时间 的要求,

 Integer milliSecond = Integer.parseInt(repeatMap.get(currUrl));
                logger.info("当前url={}进行防重校验,{}毫秒禁止重复请求!", currUrl, milliSecond);
                //用户ID
                String tUserId = userInfo.getUserId();
                Map<String, String> currThreadMap = new HashMap<String, String>();
                // 系统当前毫秒数
                Long currTime = System.currentTimeMillis();
                Long expireTime = currTime + new Long(milliSecond);
                Long count = jedisTemplate.STRINGS.setnx(Constants.INSTALLMENT_KEY + Constants.REPEAT_KEY + tUserId, expireTime.toString());
                if (count == 1L) {
                    logger.info("防重校验通过,不是重复提交!");
                    currThreadMap.put(LOCK, tUserId);
                } else {
                    logger.info("防重校验失败,是重复提交,需校验超时时间!");
                    String cacheExpTime = jedisTemplate.STRINGS.get(Constants.INSTALLMENT_KEY + Constants.REPEAT_KEY + tUserId);
                    if (StringUtils.isNotEmpty(cacheExpTime)) {
                        if (currTime > Long.valueOf(cacheExpTime)) {
                            logger.info("redis锁已超时,当前请求可以重新获取锁,currTime={}, cacheExpTime={}", currTime, cacheExpTime);
                            Long newExpTime = System.currentTimeMillis() + new Long(milliSecond);
                            String oldExpTime = jedisTemplate.STRINGS.getSet(Constants.INSTALLMENT_KEY + Constants.REPEAT_KEY + tUserId,
                                    newExpTime.toString());
                            if (!cacheExpTime.equals(oldExpTime)) {
                                logger.info("锁已经被其他线程获取,当前线程拒绝执行请求");
                                setResponse(response, ResCode.REQUEST_PROCESS);
                                return false;
                            }
                            logger.info("当前线程已经获得锁,可以执行请求");
                            currThreadMap.put(LOCK, tUserId);
                        } else {
                            logger.info("redis锁未超时,当前请求拒绝执行,currTime={}, cacheExpTime={}", currTime, cacheExpTime);
                            setResponse(response, ResCode.REQUEST_PROCESS);
                            return false;
                        }
                    } else {
                        logger.info("redis缓存时间为空,锁已解除,重新获取锁");
                        expireTime = currTime + new Long(milliSecond);
                        count = jedisTemplate.STRINGS.setnx(Constants.INSTALLMENT_KEY + Constants.REPEAT_KEY + tUserId, expireTime.toString());
                        if (count == 0L) {
                            logger.info("锁已经被其他线程获取,当前线程拒绝执行请求");
                            setResponse(response, ResCode.REQUEST_PROCESS);
                            return false;
                        }
                        logger.info("当前线程已经获得锁,可以执行请求");
                        currThreadMap.put(LOCK, tUserId);
                    }
                }
                threadMap.set(currThreadMap);

threadMap 是 一个 ThreadLocal,用于最后将 失效的 redis key清除

解决同一个请求的问题

我们使用是 zookpeer 的临时节点

   String path = publicKey[1] + "_" + params.get("platform") + "_" + params.get("outUserId");
            boolean lock = zkClient.getEphemeralLock(path);
            if (!lock) {
                logger.info("当前存在并发问题,获取锁失败,请求打回,path={}", publicKey[1]);
                throw new RequestException(OutApiResCode.REQUEST_CONCURRENT_WAINING.getCode(), "请求提交频率过高!");
            }

这一步可以在 验证参数的时候 使用,如果是 同一个 outUserId 那么就会有拦截的功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值