基于Redis的分布式限流实现

xiaoxiao2021-02-28  39

本节基于Redis实现了系统的限流控制。

Lua脚本准备

local val = redis.call('incr', KEYS[1]) local ttl = redis.call('ttl', KEYS[1]) redis.log(redis.LOG_NOTICE, "incr "..KEYS[1].." "..val); if val == 1 then redis.call('expire', KEYS[1], tonumber(ARGV[1])) else if ttl == -1 then redis.call('expire', KEYS[1], tonumber(ARGV[1])) end end if val > tonumber(ARGV[2]) then return 0 end return 1

对传入的key做incr操作,如果key首次生成,设置超时时间ARGV[1];

ttl是为防止某些key在未设置超时时间并长时间已经存在的情况下做的保护的判断;

判断当前key的val是否超过限制次数ARGV[2]。

Redis客户端

public Long limit(String key) { return redisClient.eval(key, expire, reqCount); }

加载lua脚本,并对外提供Redis调用的接口。

AOP

@Before("@annotation(com.snatch.deal.shop.common.annotations.RateLimit)") public void before(JoinPoint pjp) throws Throwable { Signature sig = pjp.getSignature(); if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用在方法上"); } MethodSignature msig = (MethodSignature) sig; String methodName = pjp.getTarget().getClass().getName() + "." + msig.getName(); String limitKey = Md5Utils.encrypt(methodName); if (rateLimiter.limit(limitKey) != 1){ throw new OverLimitException("触发限流控制"); } }

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimit { String value() default ""; }

利用AOP的before实现方法执行前的拦截,所有注解了RateLimit的方法都会被缓存限流。一旦触发限流,对外抛出异常。

统一异常处理

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(OverLimitException.class) @ResponseBody public String OverLimitExceptionHandler(OverLimitException ole){ return ole.getMessage(); } }

利用ControllerAdvice处理对应的异常的处理。

详细代码在 https://gitee.com/lawlet/shop

转载请注明原文地址: https://www.6miu.com/read-2620469.html

最新回复(0)