Java?Redisson多策略注解限流
前言
限流:使用Redisson的RRateLimiter進(jìn)行限流多策略:map+函數(shù)式接口優(yōu)化if判斷
限流:使用Redisson的RRateLimiter進(jìn)行限流
多策略:map+函數(shù)式接口優(yōu)化if判斷
自定義注解
/** * aop限流注解 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface RedisLimit { String prefix() default "rateLimit:"; //限流唯一標(biāo)示 String key() default ""; //限流單位時間(單位為s) int time() default 1; //單位時間內(nèi)限制的訪問次數(shù) int count(); //限流類型 LimitType type() default LimitType.CUSTOM; }
定義限流類型
public enum LimitType { /** * 自定義key */ CUSTOM, /** * 請求者IP */ IP, /** * 方法級別限流 * key = ClassName+MethodName */ METHOD, /** * 參數(shù)級別限流 * key = ClassName+MethodName+Params */ PARAMS, /** * 用戶級別限流 * key = ClassName+MethodName+Params+UserId */ USER, /** * 根據(jù)request的uri限流 * key = Request_uri */ REQUEST_URI, /** * 對requesturi+userId限流 * key = Request_uri+UserId */ REQUESTURI_USERID, /** * 對userId限流 * key = userId */ SINGLEUSER, /** * 對方法限流 * key = ClassName+MethodName */ SINGLEMETHOD, /** * 對uri+params限流 * key = uri+params */ REQUEST_URI_PARAMS, /** * 對uri+params+userId限流 * key = uri+params+userId */ REQUEST_URI_PARAMS_USERID; }
生成key的工具類
根據(jù)類型生成鎖的對象(key)的工具類,使用map+函數(shù)式接口優(yōu)化if,其中BaseContext
是一個獲取用戶唯一標(biāo)識userId的工具類
@Component public class ProceedingJoinPointUtil { @Autowired private HttpServletRequest request; private Map<LimitType, Function<ProceedingJoinPoint,String>> functionMap = new HashMap<>(9); @PostConstruct void initMap(){ //初始化策略 functionMap.put(LimitType.METHOD, this::getMethodTypeKey); functionMap.put(LimitType.PARAMS, this::getParamsTypeKey); functionMap.put(LimitType.USER, this::getUserTypeKey); functionMap.put(LimitType.REQUEST_URI,proceedingJoinPoint -> request.getRequestURI()); functionMap.put(LimitType.REQUESTURI_USERID, proceedingJoinPoint -> request.getRequestURI()+BaseContext.getUserId()); functionMap.put(LimitType.REQUEST_URI_PARAMS,proceedingJoinPoint -> request.getRequestURI()+getParams(proceedingJoinPoint)); functionMap.put(LimitType.REQUEST_URI_PARAMS_USERID,proceedingJoinPoint -> request.getRequestURI()+getParams(proceedingJoinPoint)+BaseContext.getUserId()); functionMap.put(LimitType.SINGLEUSER,(proceedingJoinPoint)-> String.valueOf(BaseContext.getUserId())); functionMap.put(LimitType.SINGLEMETHOD,(proceedingJoinPoint -> { StringBuilder sb = new StringBuilder(); appendMthodName(proceedingJoinPoint,sb); return sb.toString(); })); } public Object getKey(ProceedingJoinPoint joinPoint, RedisLimit redisLimit) { //根據(jù)限制類型生成key Object generateKey = ""; //自定義 if(redisLimit.type() != LimitType.CUSTOM){ generateKey = generateKey(redisLimit.type(), joinPoint); }else { //非自定義 generateKey = redisLimit.key(); } return generateKey; } /** * 根據(jù)LimitType生成key * @param type * @param joinPoint * @return */ private Object generateKey(LimitType type , ProceedingJoinPoint joinPoint) { Function function = functionMap.get(type); Object result = function.apply(joinPoint); return result; } /** * 方法級別 * key = ClassName+MethodName * @param joinPoint * @return */ private String getMethodTypeKey(ProceedingJoinPoint joinPoint){ StringBuilder sb = new StringBuilder(); appendMthodName(joinPoint, sb); return sb.toString(); } /** * 參數(shù)級別 * key = ClassName+MethodName+Params * @param joinPoint * @return */ private String getParamsTypeKey(ProceedingJoinPoint joinPoint){ StringBuilder sb = new StringBuilder(); appendMthodName(joinPoint, sb); appendParams(joinPoint, sb); return sb.toString(); } /** * 用戶級別 * key = ClassName+MethodName+Params+UserId */ private String getUserTypeKey(ProceedingJoinPoint joinPoint){ StringBuilder sb = new StringBuilder(); appendMthodName(joinPoint, sb); appendParams(joinPoint, sb); //獲取userId appendUserId(sb); return sb.toString(); } /** * StringBuilder添加類名和方法名 * @param joinPoint * @param sb */ private void appendMthodName(ProceedingJoinPoint joinPoint, StringBuilder sb) { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); sb.append(joinPoint.getTarget().getClass().getName())//類名 .append(method.getName());//方法名 } /** * StringBuilder添加方法參數(shù)值 * @param joinPoint * @param sb */ private void appendParams(ProceedingJoinPoint joinPoint, StringBuilder sb) { for (Object o : joinPoint.getArgs()) { sb.append(o.toString()); } } private String getParams(ProceedingJoinPoint joinPoint) { StringBuilder sb = new StringBuilder(); for (Object o : joinPoint.getArgs()) { if(o instanceof MultipartFile){ try { ImageTypeCheck.getImgHeightAndWidth(((MultipartFile) o).getInputStream()); } catch (IOException e) { throw new BusinessException("MultipartFile輸入流獲取失敗,source:ProceedingJoinPointUtils.149",USER_PRINCIPAL_EMAIL); } }else { sb.append(o.toString()); } } return sb.toString(); } /** * StringBuilder添加UserId * @param sb */ private void appendUserId(StringBuilder sb) { sb.append(BaseContext.getUserId()); } }
定義aop具體邏輯
@Aspect @Component @Slf4j public class RedisLimitAspect { @Autowired private RedissonClient redissonClient; @Autowired private ProceedingJoinPointUtil proceedingJoinPointUtil; @Pointcut("@annotation(com.cat.www.aop.limit.anno.RedisLimit)") private void pointCut() { } @Around("pointCut() && @annotation(redisLimit)") private Object around(ProceedingJoinPoint joinPoint, RedisLimit redisLimit) { Object generateKey = proceedingJoinPointUtil.getKey(joinPoint, redisLimit); //redis key String key = redisLimit.prefix() +generateKey.toString(); //聲明一個限流器 RRateLimiter rateLimiter = redissonClient.getRateLimiter(key); //設(shè)置速率,time秒中產(chǎn)生count個令牌 rateLimiter.trySetRate(RateType.OVERALL, redisLimit.count(), redisLimit.time(), RateIntervalUnit.SECONDS); // 試圖獲取一個令牌,獲取到返回true boolean tryAcquire = rateLimiter.tryAcquire(); if (!tryAcquire) { return new ResultData<>().FAILED().setResultIns("訪問過于頻繁"); } Object obj = null; try { obj = joinPoint.proceed(); } catch (Throwable e) { throw new RuntimeException(); } return obj; } }
到此這篇關(guān)于Java Redisson多策略注解限流的文章就介紹到這了,更多相關(guān)Java Redisson內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring Profile如何為不同環(huán)境提供不同的配置支持
這篇文章主要介紹了spring Profile如何為不同環(huán)境提供不同的配置支持,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08詳解IDEA中SpringBoot整合Servlet三大組件的過程
這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解
查看JAVA字節(jié)碼有兩種方式一種是使用 jdk命令 javap,還有一種就是 使用 插件了,今天給大家分享IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解,感興趣的朋友跟隨小編一起看看吧2021-05-05go語言題解LeetCode88合并兩個有序數(shù)組示例
這篇文章主要為大家介紹了go語言題解LeetCode88合并兩個有序數(shù)組示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12@RequestParam?和@RequestBody注解的區(qū)別解析
在 Spring MVC 中,我們可以使用 @RequestParam 和 @RequestBody 來獲取請求參數(shù),但它們在用法和作用上有一些區(qū)別,這篇文章主要介紹了@RequestParam?和@RequestBody注解的區(qū)別,需要的朋友可以參考下2023-06-06