lua脚本内容:
local c = redis.call('get',KEYS[1]) or '0'
if tonumber(c) > tonumber(ARGV[1]) then
return c end
c = redis.call('incr',KEYS[1])
if tonumber(c) == 1 then
redis.call('expire',KEYS[1],ARGV[2]) end
return c
接口限流方法:
/**
* 接口限流
*
* @param lockKey 锁key
* @param count 限制访问次数
* @param period 多少时间内,单位秒
*/
public static Long limit(String lockKey, int count, int period) {
// 执行lua脚本
Object result = staticRedisTemplate.execute(
new RedisScript<Long>() {
@Override
public String getSha1() {
return SCRIPT_LIMIT_SHA1;
}
@Override
public Class<Long> getResultType() {
return Long.class;
}
@Override
public String getScriptAsString() {
return API_LIMIT;
}
}, Collections.singletonList(lockKey), count, period);
return Long.valueOf(StrUtil.utf8Str(result));
}
自定义限流注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit {
// 资源名称,用于描述接口功能
String name() default "";
// 资源 key
String key() default "";
// key prefix
String prefix() default "";
// 时间的,单位秒
int period();
// 限制访问次数
int count();
// 限制类型
LimitType limitType() default LimitType.CUSTOMER;
}
切面:
@Around("pointcut(limit)")
public Object around(ProceedingJoinPoint joinPoint, Limit limit) throws Throwable {
HttpServletRequest request = ContextUtil.getRequest();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method signatureMethod = signature.getMethod();
LimitType limitType = limit.limitType();
String key = limit.key();
if (StrUtil.isBlank(key)) {
if (limitType == LimitType.IP) {
key = IPUtil.getIp(request);
} else {
key = signatureMethod.getName();
}
}
// 拼接key
String lockKey = StrUtil.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/", "_"));
// redis操作
Number count = RedisLockUtil.limit(lockKey, limit.count(), limit.period());
if (null != count && count.intValue() <= limit.count()) {
log.info("第{}次访问key为 {},描述为 [{}] 的接口", count.intValue(), lockKey, limit.name());
return joinPoint.proceed();
} else {
throw new BizException("访问次数受限制");
}
}
接口:
错误信息:
本该是返回6,却变成null了,奇了个怪了
解决方法:
修改lua脚本代码,返回值转换一下类型
local c = redis.call('get',KEYS[1]) or '0'
if tonumber(c) > tonumber(ARGV[1]) then
return tonumber(c) end
c = redis.call('incr',KEYS[1])
if tonumber(c) == 1 then
redis.call('expire',KEYS[1],ARGV[2]) end
return tonumber(c)
重新测试:
就此问题解决!!!