最新redis实现分布式限流 结合Lua脚本,Java开发笔试题编程题

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

lua脚本

– 下标从 1 开始

local key = KEYS[1]

local now = tonumber(ARGV[1])

local ttl = tonumber(ARGV[2])

local expired = tonumber(ARGV[3])

– 最大访问量

local max = tonumber(ARGV[4])

– 清除过期的数据

– 移除指定分数区间内的所有元素,expired 即已经过期的 score

– 根据当前时间毫秒数 - 超时毫秒数,得到过期时间 expired

redis.call(‘zremrangebyscore’, key, 0, expired)

– 获取 zset 中的当前元素个数

local current = tonumber(redis.call(‘zcard’, key))

local next = current + 1

if next > max then

– 达到限流大小 返回 0

return 0;

else

– 往 zset 中添加一个值、得分均为当前时间戳的元素,[value,score]

redis.call(“zadd”, key, now, now)

– 每次访问均重新设置 zset 的过期时间,单位毫秒

redis.call(“pexpire”, key, ttl)

return next

end

切面类

@Slf4j

@Aspect

@Component

@RequiredArgsConstructor(onConstructor_ = @Autowired)

public class RateLimiterAspect {

private final static String SEPARATOR = “:”;

private final static String REDIS_LIMIT_KEY_PREFIX = “limit:”;

private final StringRedisTemplate stringRedisTemplate;

private final RedisScript limitRedisScript;

@Pointcut(“@annotation(com.xkcoding.ratelimit.redis.annotation.RateLimiter)”)

public void rateLimit() {

}

@Around(“rateLimit()”)

public Object pointcut(ProceedingJoinPoint point) throws Throwable {

MethodSignature signature = (MethodSignature) point.getSignature();

Method method = signature.getMethod();

RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);

if (rateLimiter != null) {

String key = rateLimiter.key();

// 默认用类名+方法名做限流的 key 前缀

if (StrUtil.isBlank(key)) {

key = method.getDeclaringClass().getName() + StrUtil.DOT + method.getName();

}

String ipAddress = IpUtil.getIpAddr();

key = key + SEPARATOR + ipAddress.replaceAll(“:”, “-”);

long max = rateLimiter.max();

long timeout = rateLimiter.timeout();

TimeUnit timeUnit = rateLimiter.timeUnit();

boolean limited = shouldLimited(key, max, timeout, timeUnit);

if (limited) {

throw new RuntimeException(“手速太快了,慢点儿吧~”);

}

}

return point.proceed();

}

private boolean shouldLimited(String key, long max, long timeout, TimeUnit timeUnit) {

// 最终的 key 格式为:

// limit:自定义key:IP

// limit:类名.方法名:IP

key = REDIS_LIMIT_KEY_PREFIX + key;

// 统一使用单位毫秒

long ttl = timeUnit.toMillis(timeout);

// 当前时间毫秒数

long now = Instant.now().toEpochMilli();

long expired = now - ttl;

// 注意这里必须转为 String,否则会报错 java.lang.Long cannot be cast to java.lang.String

Long executeTimes = stringRedisTemplate.execute(limitRedisScript, Collections.singletonList(key), now + “”, ttl + “”, expired + “”, max + “”);

if (executeTimes != null) {

if (executeTimes == 0) {

log.debug(“【{}】在单位时间 {} 毫秒内已达到访问上限,当前接口上限 {}”, key, ttl, max);

return true;

} else {

log.debug(“【{}】在单位时间 {} 毫秒内访问 {} 次”, key, ttl, executeTimes);

return false;

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。

面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值