成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

接口防刷!利用Redisson快速實(shí)現(xiàn)自定義限流注解

數(shù)據(jù)庫 Redis
利用Redis的有序集合即Sorted Set數(shù)據(jù)結(jié)構(gòu),構(gòu)造一個令牌桶來實(shí)施限流。而Redisson已經(jīng)幫我們封裝成了RRateLimiter,通過Redisson,即可快速實(shí)現(xiàn)我們的目標(biāo)。

問題:

在日常開發(fā)中,一些重要的對外接口,需要添加訪問頻率限制,以免造成資產(chǎn)損失。

如登錄接口,當(dāng)用戶使用手機(jī)號+驗(yàn)證碼登錄時,一般我們會生成6位數(shù)的隨機(jī)驗(yàn)證碼,并將驗(yàn)證碼有效期設(shè)置為1-3分鐘,如果對登錄接口不加以限制,理論上,通過技術(shù)手段,快速重試100000次,即可將驗(yàn)證碼窮舉出來。

解決思路:

對登錄接口加上限流操作,如限制一分鐘內(nèi)最多登錄5次,登錄次數(shù)過多,就返回失敗提示,或者將賬號鎖定一段時間。

實(shí)現(xiàn)手段:

利用redis的有序集合即Sorted Set數(shù)據(jù)結(jié)構(gòu),構(gòu)造一個令牌桶來實(shí)施限流。而redisson已經(jīng)幫我們封裝成了RRateLimiter,通過redisson,即可快速實(shí)現(xiàn)我們的目標(biāo)。

1. 定義一個限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GlobalRateLimiter {

    String key();

    long rate();

    long rateInterval() default 1L;

    RateIntervalUnit rateIntervalUnit() default RateIntervalUnit.SECONDS;

}

2. 利用aop進(jìn)行切面

@Aspect
@Component
@Slf4j
public class GlobalRateLimiterAspect {

    @Resource
    private Redisson redisson;
    @Value("${spring.application.name}")
    private String applicationName;
    private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();

    @Pointcut(value = "@annotation(com.zj.demoshow.annotion.GlobalRateLimiter)")
    public void cut() {
    }

    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        GlobalRateLimiter globalRateLimiter = method.getDeclaredAnnotation(GlobalRateLimiter.class);
        Object[] params = joinPoint.getArgs();
        long rate = globalRateLimiter.rate();
        String key = globalRateLimiter.key();
        long rateInterval = globalRateLimiter.rateInterval();
        RateIntervalUnit rateIntervalUnit = globalRateLimiter.rateIntervalUnit();
        if (key.contains("#")) {
            ExpressionParser parser = new SpelExpressionParser();
            StandardEvaluationContext ctx = new StandardEvaluationContext();
            String[] parameterNames = discoverer.getParameterNames(method);
            if (parameterNames != null) {
                for (int i = 0; i < parameterNames.length; i++) {
                    ctx.setVariable(parameterNames[i], params[i]);
                }
            }
            Expression expression = parser.parseExpression(key);
            Object value = expression.getValue(ctx);
            if (value == null) {
                throw new RuntimeException("key無效");
            }
            key = value.toString();
        }
        key = applicationName + "_" + className + "_" + methodName + "_" + key;
        log.info("設(shè)置限流鎖key={}", key);
        RRateLimiter rateLimiter = this.redisson.getRateLimiter(key);
        if (!rateLimiter.isExists()) {
            log.info("設(shè)置流量,rate={},rateInterval={},rateIntervalUnit={}", rate, rateInterval, rateIntervalUnit);
            rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, rateIntervalUnit);
            //設(shè)置一個過期時間,避免key一直存在浪費(fèi)內(nèi)存,這里設(shè)置為延長5分鐘
            long millis = rateIntervalUnit.toMillis(rateInterval);
            this.redisson.getBucket(key).expire(Long.sum(5 * 1000 * 60, millis), TimeUnit.MILLISECONDS);
        }
        boolean acquire = rateLimiter.tryAcquire(1);
        if (!acquire) {
            //這里直接拋出了異常  也可以拋出自定義異常,通過全局異常處理器攔截進(jìn)行一些其他邏輯的處理
            throw new RuntimeException("請求頻率過高,此操作已被限制");
        }
        return joinPoint.proceed();
    }
}

ok,通過以上兩步,即可完成我們的限流注解了,下面通過一個接口驗(yàn)證下效果。

新建一個controller,寫一個模擬登錄的方法。

@RestController
@RequestMapping(value = "/user")
public class UserController {


    @PostMapping(value = "/testForLogin")
    //以account為鎖的key,限制每分鐘最多登錄5次
    @GlobalRateLimiter(key = "#params.account", rate = 5, rateInterval = 60)
    R<Object> testForLogin(@RequestBody @Validated LoginParams params) {
        //登錄邏輯
        return R.success("登錄成功");
    }
}

啟動服務(wù),通過postman訪問此接口進(jìn)行驗(yàn)證。

可以看到,在第6次訪問接口的時候,拋出了請求限制的異常。

注意點(diǎn):

設(shè)置key的時候,一定要注意唯一性,比如登錄接口,可以將登錄賬號作為唯一性,查詢某個人的訂單記錄時,將用戶id作為唯一性,要避免無意義的key,以免誤造成全局接口的限流。

設(shè)置rateLimiter的rate時,RateType有兩種模式:全局 or 客戶端,可以根據(jù)需求自主設(shè)置,一般都使用全局。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2024-04-01 08:11:20

2024-06-14 09:30:58

2023-10-24 13:48:50

自定義注解舉值驗(yàn)證

2022-11-01 11:15:56

接口策略模式

2023-03-03 09:11:12

高并發(fā)SpringBoot

2022-12-13 09:19:06

高并發(fā)SpringBoot

2021-12-30 12:30:01

Java注解編譯器

2024-12-27 15:37:23

2015-02-12 15:33:43

微信SDK

2024-07-02 11:42:53

SpringRedis自定義

2022-02-17 07:10:39

Nest自定義注解

2024-10-09 10:46:41

springboot緩存redis

2023-09-04 08:12:16

分布式鎖Springboot

2017-08-03 17:00:54

Springmvc任務(wù)執(zhí)行器

2023-10-09 07:37:01

2024-10-14 17:18:27

2015-02-12 15:38:26

微信SDK

2009-08-21 15:38:45

ControllerF

2023-10-11 07:57:23

springboot微服務(wù)

2011-03-31 16:03:20

cacti性能計(jì)數(shù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 亚洲一区二区av | 亚洲丝袜天堂 | 国产在线一区观看 | 欧美日日 | 一区二区三区精品视频 | 精品一区二区三区日本 | 超碰成人在线观看 | 成人免费精品视频 | 国产欧美一区二区三区在线播放 | 久久婷婷色 | 成人a在线 | 亚洲一区二区免费视频 | 欧美精品乱码久久久久久按摩 | 日韩欧美中文字幕在线观看 | 97在线超碰 | 一级片在线视频 | 国产精品九九 | av免费网站在线观看 | 精品中文在线 | 在线一区视频 | 久久亚洲春色中文字幕久久久 | 久久中文字幕一区 | 久久69精品久久久久久久电影好 | 亚洲欧洲综合av | 毛片区| 国产亚洲精品91 | 成人av高清| 美女黄网站视频免费 | 国产成人一区二区三区久久久 | 日韩在线视频免费观看 | 特黄色毛片 | 中文成人在线 | 免费在线观看成人av | 欧美精品在线免费观看 | 亚洲精品电影网在线观看 | 欧美专区在线观看 | 日韩人体在线 | 成人性视频免费网站 | 欧美理论在线观看 | 国产美女高潮 | 亚洲欧美国产精品久久 |