Java?Redisson多策略注解限流
前言
限流:使用Redisson的RRateLimiter進行限流多策略:map+函數(shù)式接口優(yōu)化if判斷
限流:使用Redisson的RRateLimiter進行限流
多策略: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)境提供不同的配置支持,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08
詳解IDEA中SpringBoot整合Servlet三大組件的過程
這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解
查看JAVA字節(jié)碼有兩種方式一種是使用 jdk命令 javap,還有一種就是 使用 插件了,今天給大家分享IDEA中使用jclasslib插件可視化方式查看類字節(jié)碼的過程詳解,感興趣的朋友跟隨小編一起看看吧2021-05-05
go語言題解LeetCode88合并兩個有序數(shù)組示例
這篇文章主要為大家介紹了go語言題解LeetCode88合并兩個有序數(shù)組示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
@RequestParam?和@RequestBody注解的區(qū)別解析
在 Spring MVC 中,我們可以使用 @RequestParam 和 @RequestBody 來獲取請求參數(shù),但它們在用法和作用上有一些區(qū)別,這篇文章主要介紹了@RequestParam?和@RequestBody注解的區(qū)別,需要的朋友可以參考下2023-06-06

