• 自定义注解和使用


    1、使用@interface关键定义注解(RateLimiter.java),如下:

    package com.vx.servicehi.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @author wangbs
     * @version 1.0
     * @date 2019/12/16 1:25
     * @className RateLimiter
     * @desc 限流注解
     */
    
    //注解作用域
    //        ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
    //        ElementType.FIELD:允许作用在属性字段上
    //        ElementType.METHOD:允许作用在方法上
    //        ElementType.PARAMETER:允许作用在方法参数上
    //        ElementType.CONSTRUCTOR:允许作用在构造器上
    //        ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
    //        ElementType.ANNOTATION_TYPE:允许作用在注解上
    //        ElementType.PACKAGE:允许作用在包上
    //
    //        注解的生命周期
    //        RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
    //    RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
    //    RetentionPolicy.RUNTIME:永久保存,可以反射获取
    
    // 注解的作用域
    @Target(ElementType.METHOD)
    // 注解的生命周期
    @Retention(RetentionPolicy.RUNTIME)
    // 允许子类继承
    @Inherited
    // 生成javadoc的时候生成注解的信息
    @Documented
    public @interface RateLimiter {
    
        /**
         * 限流key
         * @return
         */
        String key() default "rate:limiter";
        /**
         * 单位时间限制通过请求数
         * @return
         */
        long limit() default 10;
    
        /**
         * 过期时间,单位秒
         * @return
         */
        long expire() default 1;
    
        /**
         * 限流提示语
         * @return
         */
        String message() default "false";
    }

    2、在普通类上使用注解,使用方法

    
    
    package com.vx.servicehi.controller;

    import com.vx.servicehi.annotation.RateLimiter;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("business")
    public class BusinessController {
    private static final Logger LOGGER = LoggerFactory.getLogger(BusinessController.class);

    private static final String MESSAGE = "{"code":"400","msg":"FAIL","desc":"触发限流"}";


    @Value("${server.port}")
    String port;

    /**
    * 使用自定义注解 限流
    * @param name
    * @return
    */
    @RequestMapping("/hi")
    @RateLimiter(key = "business/hi", limit = 5, expire = 10, message = MESSAGE)
    public String home(@RequestParam(value = "name", defaultValue = "forezp") String name) {
    return "hi " + name + " ,i am from port:" + port;
    }
    }
     

    3. 定义切面

    package com.vx.servicehi.handler;
    
    import com.vx.servicehi.annotation.RateLimiter;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    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.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.script.DefaultRedisScript;
    import org.springframework.scripting.support.ResourceScriptSource;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author wangbs
     * @version 1.0
     * @date 2019/12/16 1:17
     * @className RateLimterHandler
     * @desc 限流处理器
     */
    @Aspect
    @Component
    public class RateLimterHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(RateLimterHandler.class);
    
        @Autowired
        RedisTemplate redisTemplate;
    
        private DefaultRedisScript<Long> getRedisScript;
    
        @PostConstruct
        public void init() {
            getRedisScript = new DefaultRedisScript<>();
            getRedisScript.setResultType(Long.class);
            getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimter.lua")));
            LOGGER.info("RateLimterHandler[分布式限流处理器]脚本加载完成");
        }
    
        @Pointcut("@annotation(com.vx.servicehi.annotation.RateLimiter)")
        public void rateLimiter() {}
    
        @Around("@annotation(rateLimiter)")
        public Object around(ProceedingJoinPoint proceedingJoinPoint, RateLimiter rateLimiter) throws Throwable {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("RateLimterHandler[分布式限流处理器]开始执行限流操作");
            }
            Signature signature = proceedingJoinPoint.getSignature();
            if (!(signature instanceof MethodSignature)) {
                throw new IllegalArgumentException("the Annotation @RateLimter must used on method!");
            }
            /**
             * 获取注解参数
             */
            // 限流模块key
            String limitKey = rateLimiter.key();
            if(StringUtils.isBlank(limitKey)){
                throw new NullPointerException();
            }
            // 限流阈值
            long limitTimes = rateLimiter.limit();
            // 限流超时时间
            long expireTime = rateLimiter.expire();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("RateLimterHandler[分布式限流处理器]参数值为-limitTimes={},limitTimeout={}", limitTimes, expireTime);
            }
            // 限流提示语
            String message = rateLimiter.message();
            if (StringUtils.isBlank(message)) {
                message = "false";
            }
            /**
             * 执行Lua脚本
             */
            List<String> keyList = new ArrayList();
            // 设置key值为注解中的值
            keyList.add(limitKey);
            /**
             * 调用脚本并执行
             */
            Long result = (Long) redisTemplate.execute(getRedisScript, keyList, expireTime, limitTimes);
            if (result == 0) {
                String msg = "由于超过单位时间=" + expireTime + "-允许的请求次数=" + limitTimes + "[触发限流]";
                LOGGER.debug(msg);
                return message;
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("RateLimterHandler[分布式限流处理器]限流执行结果-result={},请求[正常]响应", result);
            }
            return proceedingJoinPoint.proceed();
        }
    }
    

      

    4. 在resource 下定义文件 rateLimter.lua

    --获取KEY
    local key1 = KEYS[1]
    --给指定的key 值增加一,如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作
    local val = redis.call('incr', key1)
    --以秒为单位返回 key 的剩余过期时间
    local ttl = redis.call('ttl', key1)
    
    --获取ARGV内的参数并打印
    local expire = ARGV[1]
    local times = ARGV[2]
    
    redis.log(redis.LOG_DEBUG,tostring(times))
    redis.log(redis.LOG_DEBUG,tostring(expire))
    
    redis.log(redis.LOG_NOTICE, "incr "..key1.." "..val);
    if val == 1 then
        redis.call('expire', key1, tonumber(expire))
    else
        if ttl == -1 then
    	--expire当key不存在或者不能为key设置生存时间时返回  0
            redis.call('expire', key1, tonumber(expire))
        end
    end
    
    if val > tonumber(times) then
        return 0
    end
    
    return 1
    

      

    5、解析注解,通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑

    
    
    package com.vx.servicehi.annotation;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;

    /**
    * 解析注解
    * 通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
    *
    * 对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
    getAnnotation:返回指定的注解
    isAnnotationPresent:判定当前元素是否被指定注解修饰
    getAnnotations:返回所有的注解
    getDeclaredAnnotation:返回本元素的指定注解
    getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
    *
    * @author wangbs
    * @date 2019-12-21 22:52:42
    * 主要是对自定义注解 RateLimiter 的获取 测试
    *
    */
    public class ParseDecription {

    public static void main(String[] args) {
    // TODO Auto-generated method stub
    // 1、使用类加载器加载类
    try {
    Class c = Class.forName("com.vx.servicehi.controller.BusinessController");
    System.out.println(c);

    // 2、找到类上面的注解
    boolean isExist = c.isAnnotationPresent(RateLimiter.class);

    if(isExist) {
    // 3、拿到注解实例
    RateLimiter d = (RateLimiter) c.getAnnotation(RateLimiter.class);
    System.out.println("========parse class annotation=========");
    System.out.println("desc = " + d.key());
    System.out.println("author = " + d.message());
    System.out.println("age = " + d.limit());
    }

    // 4、找到方法上的注解
    Method[] ms = c.getMethods();
    for (Method m : ms) {
    boolean isMExist = m.isAnnotationPresent(RateLimiter.class);
    if(isMExist) {
    RateLimiter d = m.getAnnotation(RateLimiter.class);
    System.out.println("========parse method annotation=========");
    System.out.println("desc = " + d.key());
    System.out.println("author = " + d.message());
    System.out.println("age = " + d.limit());
    }
    }

    // 另外一种解析方法
    for (Method m : ms) {
    Annotation[] annotations = m.getAnnotations();
    for (Annotation annotation : annotations) {
    if(annotation instanceof RateLimiter) {
    System.out.println("========parse method annotation other way=========");
    RateLimiter d = (RateLimiter) annotation;
    System.out.println("desc = " + d.key());
    System.out.println("author = " + d.message());
    System.out.println("age = " + d.limit());
    }
    }
    }

    } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
  • 相关阅读:
    Linux 7 web服务基础知识
    Linux 6 Nginx
    Linux 5 MySQL、redis相关
    Linux 4 安装相关程序
    phpcms 路由配置
    ecmall 入口文件解析 引入了什么
    php 调用天气接口
    phpcms 加载微信类库,生成签名
    ecmall 学习记录2
    Jquery 遍历
  • 原文地址:https://www.cnblogs.com/xiaowangbangzhu/p/12071841.html
Copyright © 2020-2023  润新知