Java Redisson多策略注解限流


Posted in Java/Android onSeptember 23, 2022

前言

限流:使用Redisson的RRateLimiter进行限流多策略:map+函数式接口优化if判断

限流:使用Redisson的RRateLimiter进行限流

多策略:map+函数式接口优化if判断

自定义注解

/**
 * aop限流注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLimit {


    String prefix() default "rateLimit:";

    //限流唯一标示
    String key() default "";

    //限流单位时间(单位为s)
    int time() default 1;

    //单位时间内限制的访问次数
    int count();

    //限流类型
    LimitType type() default LimitType.CUSTOM;

}

定义限流类型

public enum LimitType {

    /**
     * 自定义key
     */
    CUSTOM,

    /**
     * 请求者IP
     */
    IP,

    /**
     * 方法级别限流
     * key = ClassName+MethodName
     */
    METHOD,

    /**
     * 参数级别限流
     * key = ClassName+MethodName+Params
     */
    PARAMS,

    /**
     * 用户级别限流
     * key = ClassName+MethodName+Params+UserId
     */
    USER,

    /**
     * 根据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的工具类

根据类型生成锁的对象(key)的工具类,使用map+函数式接口优化if,其中BaseContext是一个获取用户唯一标识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) {
        //根据限制类型生成key
        Object generateKey = "";
        //自定义
        if(redisLimit.type() != LimitType.CUSTOM){
            generateKey = generateKey(redisLimit.type(), joinPoint);
        }else {
            //非自定义
            generateKey = redisLimit.key();
        }
        return generateKey;
    }

    /**
     * 根据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();
    }



    /**
     * 参数级别
     * 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添加方法参数值
     * @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);

        //设置速率,time秒中产生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;
    }
}

到此这篇关于Java Redisson多策略注解限流的文章就介绍到这了,更多相关Java Redisson内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
解决SpringBoot跨域的三种方式
Jun 26 Java/Android
elasticSearch-api的具体操作步骤讲解
Jun 28 Java/Android
Java 泛型详解(超详细的java泛型方法解析)
Jul 02 Java/Android
Java基础-封装和继承
Jul 02 Java/Android
dubbo服务整合zipkin详解
Jul 26 Java/Android
Java中CyclicBarrier和CountDownLatch的用法与区别
Aug 23 Java/Android
Spring Cloud 中@FeignClient注解中的contextId属性详解
Sep 25 Java/Android
SpringBoot2零基础到精通之异常处理与web原生组件注入
Mar 22 Java/Android
MybatisPlus EntityWrapper如何自定义SQL
Mar 22 Java/Android
SpringBoot2零基础到精通之数据与页面响应
Mar 22 Java/Android
前端与RabbitMQ实时消息推送未读消息小红点实现示例
Jul 23 Java/Android
java实现web实时消息推送的七种方案
Jul 23 Java/Android
Android移动应用开发指南之六种布局详解
Sep 23 #Java/Android
Java中的Kafka为什么性能这么快及4大核心详析
Mybatis 一级缓存和二级缓存原理区别
Sep 23 #Java/Android
Java实现贪吃蛇游戏的示例代码
Sep 23 #Java/Android
Java获取字符串编码格式实现思路
Sep 23 #Java/Android
java获取一个文本文件的编码(格式)信息
Sep 23 #Java/Android
JDK8中String的intern()方法实例详细解读
Sep 23 #Java/Android
You might like
中东人咖啡哲学
2021/03/03 咖啡文化
PHP中基于ts与nts版本- vc6和vc9编译版本的区别详解
2013/04/26 PHP
ThinkPHP5框架中使用JWT的方法示例
2020/06/03 PHP
JavaScript语句可以不以;结尾的烦恼
2007/03/08 Javascript
自己整理的一个javascript日期处理函数
2010/10/16 Javascript
仅IE支持clearAttributes/mergeAttributes方法使用介绍
2012/05/04 Javascript
对table和ul实现js分页示例分享
2014/02/24 Javascript
JS和函数式语言的三特性
2014/03/05 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
js实现文字闪烁特效的方法
2015/12/17 Javascript
浅析jQuery事件之on()方法绑定多个选择器,多个事件
2016/04/27 Javascript
详解nodejs 文本操作模块-fs模块(四)
2016/12/22 NodeJs
PHP实现记录代码运行时间封装类实例教程
2017/05/08 Javascript
使用node.js对音视频文件加密的实例代码
2017/08/30 Javascript
jQuery实现的简单对话框拖动功能示例
2018/06/05 jQuery
AngularJS实现动态切换样式的方法分析
2018/06/26 Javascript
vue中多个倒计时实现代码实例
2019/03/27 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
[01:03:37]Secret vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
浅析python 内置字符串处理函数的使用方法
2014/06/11 Python
python多线程threading.Lock锁用法实例
2014/11/01 Python
Python爬虫实例_利用百度地图API批量获取城市所有的POI点
2018/01/10 Python
python微信公众号开发简单流程
2018/03/23 Python
基于numpy.random.randn()与rand()的区别详解
2018/04/17 Python
python修改文件内容的3种方法详解
2019/11/15 Python
基于TensorFlow常量、序列以及随机值生成实例
2020/01/04 Python
python 实现字符串下标的输出功能
2020/02/13 Python
css3+伪元素实现鼠标移入时下划线向两边展开的效果
2017/04/25 HTML / CSS
详解HTML5之pushstate、popstate操作history,无刷新改变当前url
2017/03/15 HTML / CSS
英国银首饰公司:e&e Jewellery
2021/02/11 全球购物
日本最大的彩色隐形眼镜销售网站:CharmColor
2020/09/09 全球购物
英语专业毕业生求职简历的自我评价
2013/10/24 职场文书
市场开发计划书
2014/05/07 职场文书
自我检讨报告
2015/01/28 职场文书
2019入党申请书范文3篇
2019/08/21 职场文书
Angular CLI发布路径的配置项浅析
2021/03/29 Javascript