最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

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

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

定義一個限流注解

  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無效");
  			}
  			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一直存在浪費內(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,通過以上兩步,即可完成我們的限流注解了,下面通過一個接口驗證下效果。

新建一個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)行驗證。

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

相關(guān)文章

  • Redis keys命令的具體使用

    Redis keys命令的具體使用

    本文主要介紹了Redis keys命令的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 用Redis實現(xiàn)微博關(guān)注關(guān)系

    用Redis實現(xiàn)微博關(guān)注關(guān)系

    在微博中,每一個用戶都會有一個關(guān)注列表,一個粉絲列表。用戶可以查看自己的關(guān)注,粉絲列表,也可以查看別人的關(guān)注,粉絲列表。并且,要展示列表里每個人與當(dāng)前查看者的關(guān)注狀態(tài)。
    2015-09-09
  • Redis實現(xiàn)分布式事務(wù)的示例

    Redis實現(xiàn)分布式事務(wù)的示例

    Redis雖不支持傳統(tǒng)SQL數(shù)據(jù)庫ACID特性的事務(wù),但提供了事務(wù)特性,允許多命令捆綁執(zhí)行,通過命令MULTI、EXEC、DISCARD、WATCH實現(xiàn),感興趣的可以了解一下
    2024-10-10
  • Redis源碼分析之set?和?sorted?set?使用

    Redis源碼分析之set?和?sorted?set?使用

    本文介紹了Redis?中的?set?和?sorted?set?使用源碼實現(xiàn)分析,Redis?的?Set?是?String?類型的無序集合,集合成員是唯一的,sorted?set有序集合和集合一樣也是?string?類型元素的集合,對Redis?set?和?sorted?set使用相關(guān)知識感興趣的朋友一起看看吧
    2022-03-03
  • Redis簡易延時隊列的實現(xiàn)示例

    Redis簡易延時隊列的實現(xiàn)示例

    在實際的業(yè)務(wù)場景中,經(jīng)常會遇到需要延時處理的業(yè)務(wù),本文就來介紹有下Redis簡易延時隊列的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Redis使用bloom-filter過濾器實現(xiàn)推薦去重

    Redis使用bloom-filter過濾器實現(xiàn)推薦去重

    這篇文章主要介紹了Redis使用bloom-filter過濾器實現(xiàn)推薦去重,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • redis事務(wù)如何解決超賣問題

    redis事務(wù)如何解決超賣問題

    使用Redis事務(wù)可以有效避免超賣問題,首先,通過MULTI命令開啟事務(wù),將需要執(zhí)行的多個命令加入到事務(wù)中,然后通過EXEC命令提交事務(wù),確保這些命令可以一次性、順序地執(zhí)行,在事務(wù)執(zhí)行期間,Redis服務(wù)器不會執(zhí)行其他客戶端的命令
    2024-11-11
  • Redis+aop實現(xiàn)接口防刷(冪等)的解決方案

    Redis+aop實現(xiàn)接口防刷(冪等)的解決方案

    在高并發(fā)場景下,可能會因為網(wǎng)絡(luò)或者服務(wù)器原因,造成延遲,同時就是有可能會有人用腳本大量訪問你的接口,造成資源崩潰,所以本文給大家介紹了Redis+aop實現(xiàn)接口防刷(冪等)的解決方案,需要的朋友可以參考下
    2024-03-03
  • redis緩存存儲Session原理機(jī)制

    redis緩存存儲Session原理機(jī)制

    這篇文章主要為大家介紹了redis緩存存儲Session原理機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • Redis動態(tài)熱點數(shù)據(jù)緩存策略設(shè)計

    Redis動態(tài)熱點數(shù)據(jù)緩存策略設(shè)計

    本文主要介紹了Redis動態(tài)熱點數(shù)據(jù)緩存策略設(shè)計,包括熱點數(shù)據(jù)識別、動態(tài)緩存、多級緩存、預(yù)加載機(jī)制、更新策略以及監(jiān)控告警等,具有一定的參考價值,感興趣的可以了解一下
    2025-01-01

最新評論

文水县| 乐陵市| 云梦县| 彭阳县| 武义县| 田东县| 青田县| 和龙市| 福州市| 蒲江县| 文登市| 萍乡市| 常德市| 鄂托克旗| 新营市| 永济市| 永宁县| 行唐县| 元谋县| 红原县| 蛟河市| 潞西市| 邵阳市| 凉城县| 宜章县| 邢台县| 虞城县| 本溪| 察雅县| 长寿区| 崇礼县| 安阳市| 秦皇岛市| 明星| 安溪县| 岐山县| 方正县| 开阳县| 琼中| 左云县| 黑龙江省|