zoukankan      html  css  js  c++  java
  • 原创 redis实现接口限流

    1 核心aop类

    @Slf4j
    @Aspect
    @Component
    public class LimitLptAspect {

    public static final String LUASCRIPT;
    static {
    	StringBuilder sb = new StringBuilder();
    	sb.append("local lasttime = redis.call('get', KEYS[1]);");
        sb.append("
     ").append("if lasttime then");
        sb.append("
     ").append("local addcount = (tonumber(ARGV[3]) - tonumber(lasttime))/1000");
        sb.append("
     ").append("local all = redis.call('incrby', KEYS[2], tonumber(ARGV[1]) * math.floor(addcount))");
        sb.append("
     ").append("if all > tonumber(ARGV[2]) then");
        sb.append("
     ").append("redis.call('set', KEYS[2], ARGV[2])");
        sb.append("
     ").append("end");
        sb.append("
     ").append("else");
        sb.append("
     ").append("redis.call('set', KEYS[2], ARGV[2])");
        sb.append("
     ").append("end");
        sb.append("
     ").append("local left = tonumber(redis.call('decr', KEYS[2]))");
        sb.append("
     ").append("if left >= 0 then");
        sb.append("
     ").append("redis.call('set', KEYS[1], ARGV[3])");
        sb.append("
     ").append("else");
        sb.append("
     ").append("redis.call('set', KEYS[2], 0)");
        sb.append("
     ").append("end");
        sb.append("
     ").append("redis.call('EXPIRE', KEYS[1], 600)");
        sb.append("
     ").append("redis.call('EXPIRE', KEYS[2], 600)");
        sb.append("
     ").append("return left");
        LUASCRIPT = sb.toString();
    }
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Pointcut("@annotation(cn.ascleway.hmjt.server.annotation.LimitLpt)")
    public void pointcut() {
    }
    
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取到 HttpServletRequest
    	ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Method method = resolveMethod(point);
        // 获取到注解
        LimitLpt limitAnnotation = method.getAnnotation(LimitLpt.class);
        // 在 redis 中需要使用这个 name 拼接 key
        String name = limitAnnotation.name();
        Integer maxcount = limitAnnotation.maxCount();
        Integer incount = limitAnnotation.inCount();
        String ip = IpUtils.getIpAddr2(request);
        String key;
        LimitType limitType = limitAnnotation.limitType(); // 如不设置则为全局限制  如需按用户 则需要方法参数传入用户id
        switch (limitType) {
            case IP:
                key = limitAnnotation.key() + ip;
                break;
            case CUSTOMER:
                key = limitAnnotation.key();
                break;
            default:
                key = StringUtils.upperCase(method.getName());
        }
        long curtime = System.currentTimeMillis();
        List<String> keys = ImmutableList.of(key + "lasttime", key);
        RedisScript<Number> redisScript = new DefaultRedisScript<>(LUASCRIPT, Number.class);
        Number left = redisTemplate.execute(redisScript, keys, incount, maxcount, curtime);
        log.info("请求ip{},令牌桶key为 {},描述为 [{}] 的接口,剩余数量{}", ip, key, name, left);
        if (left != null && left.intValue() >= 0) { // 使用成功则更新使用时间
            return point.proceed();
        } else {
            throw new Exception("访问频率过高,请一分钟后再试");
        }
    }
    
    private Method resolveMethod(ProceedingJoinPoint joinPoint) {
    	Signature signature = joinPoint.getSignature();//获取执行签名信息
    	MethodSignature methodSignature = (MethodSignature) signature;
    	return methodSignature.getMethod();
    } }
    

    2 controller方法添加注解

    @RestController
    @RequestMapping(value="")
    @Api(tags = "")
    public class WxHomeController {

    private static Logger logger = LoggerFactory.getLogger(WxHomeController.class);
    
    @Autowired
    private WxHomeService wxHomeService;
    
    	
    @ApiOperation(value = "栏目类别")
    @GetMapping("/artCategorys")
    @LimitLpt(key = "artCategorys", maxCount = 10, inCount = 1, limitType = LimitType.IP)
    public CommonResult<List<CategoryVo>> artCategorys() {
    	try {
    		return CommonResult.success(wxHomeService.artCategorys());
    	} catch (Exception e) {
    		logger.error("artCategorys.error", e);
    		return CommonResult.failed(e.getMessage());
    	}
    }
    }
    

    令牌桶实现,无需定时器添加令牌,请求时先新增令牌再取令牌。简单实现完成

    化繁为简,极致高效。 所有代码为本人原创,转载请联系本人。
  • 相关阅读:
    C# 抽象方法和虚方法的区别
    xmlhttprequest readyState 属性的五种状态
    ServiceStack破解文件
    k8s部署mysql
    docker 开放2376端口的问题
    .net core 发布到IIS 没有 web.config 文件
    1064
    docker mysql 主从同步配置
    Docker 鼠标在虚拟机与主机之间自由切换
    Socket原理解析2
  • 原文地址:https://www.cnblogs.com/crissblog/p/14862526.html
Copyright © 2011-2022 走看看