欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2024年07月19日 09:21:36   作者:二價(jià)亞鐵  
利用redis的有序集合即Sorted?Set數(shù)據(jù)結(jié)構(gòu),構(gòu)造一個(gè)令牌桶來(lái)實(shí)施限流,而redisson已經(jīng)幫我們封裝成了RRateLimiter,通過(guò)redisson,即可快速實(shí)現(xiàn)我們的目標(biāo),這篇文章主要介紹了利用redisson快速實(shí)現(xiàn)自定義限流注解,需要的朋友可以參考下

問(wèn)題:
在日常開(kāi)發(fā)中,一些重要的對(duì)外接口,需要加上訪問(wèn)頻率限制,以免造成資損失。

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

解決思路:對(duì)登錄接口加上限流操作,如限制一分鐘內(nèi)最多登錄5次,登錄次數(shù)過(guò)多,就返回失敗提示,或者將賬號(hào)鎖定一段時(shí)間。

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

定義一個(gè)限流注解

  import org.redisson.api.RateIntervalUnit;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface GlobalRateLimiter {
  	String key();
  	long rate();
  	long rateInterval() default 1L;
  	RateIntervalUnit rateIntervalUnit() default RateIntervalUnit.SECONDS;
  }

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

  import com.zj.demoshow.annotion.GlobalRateLimiter;
  import lombok.extern.slf4j.Slf4j;
  import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.annotation.Around;
  import org.aspectj.lang.annotation.Aspect;
  import org.aspectj.lang.annotation.Pointcut;
  import org.aspectj.lang.reflect.MethodSignature;
  import org.redisson.Redisson;
  import org.redisson.api.RRateLimiter;
  import org.redisson.api.RateIntervalUnit;
  import org.redisson.api.RateType;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.core.DefaultParameterNameDiscoverer;
  import org.springframework.expression.Expression;
  import org.springframework.expression.ExpressionParser;
  import org.springframework.expression.spel.standard.SpelExpressionParser;
  import org.springframework.expression.spel.support.StandardEvaluationContext;
  import org.springframework.stereotype.Component;
  import javax.annotation.Resource;
  import java.lang.reflect.Method;
  import java.util.concurrent.TimeUnit;
  @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無(wú)效");
  			}
  			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è)置一個(gè)過(guò)期時(shí)間,避免key一直存在浪費(fèi)內(nèi)存,這里設(shè)置為延長(zhǎng)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) {
  			//這里直接拋出了異常  也可以拋出自定義異常,通過(guò)全局異常處理器攔截進(jìn)行一些其他邏輯的處理
  			throw new RuntimeException("請(qǐng)求頻率過(guò)高,此操作已被限制");
  		}
  		return joinPoint.proceed();
  	}
  }

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

新建一個(gè)controller,寫(xiě)一個(gè)模擬登錄的方法。

@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("登錄成功");
	}
}

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

到此這篇關(guān)于利用redisson快速實(shí)現(xiàn)自定義限流注解的文章就介紹到這了,更多相關(guān)redisson自定義限流注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis增減庫(kù)存避坑的實(shí)現(xiàn)

    Redis增減庫(kù)存避坑的實(shí)現(xiàn)

    在電商平臺(tái)或者倉(cāng)庫(kù)管理系統(tǒng)中,庫(kù)存的管理是非常重要的一項(xiàng)任務(wù),本文主要介紹了Redis增減庫(kù)存避坑的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 防止redis內(nèi)存溢出優(yōu)化方法

    防止redis內(nèi)存溢出優(yōu)化方法

    本文主要介紹了防止redis內(nèi)存溢出優(yōu)化方法,包括使用maxmemory-policy選項(xiàng)、設(shè)置數(shù)據(jù)過(guò)期時(shí)間和配置Redis集群等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • Redis高并發(fā)超賣(mài)問(wèn)題解決方案圖文詳解

    Redis高并發(fā)超賣(mài)問(wèn)題解決方案圖文詳解

    Redis是一種基于內(nèi)存的數(shù)據(jù)存儲(chǔ)系統(tǒng),被廣泛用于解決高并發(fā)問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Redis高并發(fā)超賣(mài)問(wèn)題解決方案的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • 如何在centos中安裝redis插件bloom-filter

    如何在centos中安裝redis插件bloom-filter

    布隆過(guò)濾器在第一次add的時(shí)候自動(dòng)創(chuàng)建基于默認(rèn)參數(shù)的過(guò)濾器,Redis還提供了自定義參數(shù)的布隆過(guò)濾器,下面這篇文章主要給大家介紹了關(guān)于如何在centos中安裝redis插件bloom-filter的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • 如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制

    如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制

    文章討論了如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制,以支持高并發(fā)服務(wù),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • Redis優(yōu)惠券秒殺解決方案

    Redis優(yōu)惠券秒殺解決方案

    這篇文章主要介紹了Redis解決優(yōu)惠券秒殺應(yīng)用案例,本文先講了搶購(gòu)問(wèn)題,指出其中會(huì)出現(xiàn)的多線程問(wèn)題,提出解決方案采用悲觀鎖和樂(lè)觀鎖兩種方式進(jìn)行實(shí)現(xiàn),然后發(fā)現(xiàn)在搶購(gòu)過(guò)程中容易出現(xiàn)一人多單現(xiàn)象,需要的朋友可以參考下
    2022-12-12
  • Redis的過(guò)期鍵刪除策略原理說(shuō)明

    Redis的過(guò)期鍵刪除策略原理說(shuō)明

    這篇文章主要介紹了Redis的過(guò)期鍵刪除策略原理說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Redis如何使用zset處理排行榜和計(jì)數(shù)問(wèn)題

    Redis如何使用zset處理排行榜和計(jì)數(shù)問(wèn)題

    Redis的ZSET數(shù)據(jù)結(jié)構(gòu)非常適合處理排行榜和計(jì)數(shù)問(wèn)題,它可以在高并發(fā)的點(diǎn)贊業(yè)務(wù)中高效地管理點(diǎn)贊的排名,并且由于ZSET的排序特性,可以輕松實(shí)現(xiàn)根據(jù)點(diǎn)贊數(shù)實(shí)時(shí)排序的功能
    2025-02-02
  • Redis使用Lua腳本命令詳解

    Redis使用Lua腳本命令詳解

    這篇文章主要為大家介紹了Redis使用Lua腳本命令詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 深度解析Redis?數(shù)據(jù)淘汰策略

    深度解析Redis?數(shù)據(jù)淘汰策略

    本文將深入剖析8種淘汰策略的機(jī)制,并結(jié)合Java代碼演示生產(chǎn)環(huán)境的最佳實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04

最新評(píng)論