使用 redis 实现分布式接口限流注解 RedisLimit

这篇具有很好参考价值的文章主要介绍了使用 redis 实现分布式接口限流注解 RedisLimit。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

  • 很多时候,由于种种不可描述的原因,我们需要针对单个接口实现接口限流,防止访问次数过于频繁。这里就用 redis+aop 实现一个限流接口注解

@RedisLimit 代码

点击查看RedisLimit注解代码

import java.lang.annotation.*;

/**
 * 功能:分布式接口限流注解
 * @author love ice
 * @create 2023-09-18 15:43
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLimit {
    /**
     * redis中唯一key,一般用方法名字做区分
     * 作用: 针对不同接口,做不同的限流控制
     */
    String key() default "";

    /**
     * 限流时间内允许访问次数 默认1
     */
    long permitsPerSecond() default 1;

    /**
     * 限流时间,单位秒 默认60秒
     */
    long expire() default 60;

    /**
     * 限流提示信息
     */
    String msg() default "接口限流,请稍后重试";
}

AOP代码

点击查看aop代码

import com.aliyuncs.utils.StringUtils;
import com.test.redis.Infrastructure.annotation.RedisLimit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * redis限流切面
 *
 * @author love ice
 * @create 2023-09-18 15:44
 */
@Slf4j
@Aspect
@Component
public class RedisLimitAop {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private DefaultRedisScript<Long> redisScript;

    @PostConstruct
    public void init() {
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        // 执行 lua 脚本
        ResourceScriptSource resourceScriptSource = new ResourceScriptSource(new ClassPathResource("rateLimiter.lua"));
        redisScript.setScriptSource(resourceScriptSource);
    }

    @Pointcut("@annotation(com.test.redis.Infrastructure.annotation.RedisLimit)")
    private void check() {

    }

    @Before("check()")
    private void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 拿到 RedisLimit 注解,如果存在则说明需要限流
        RedisLimit redisLimit = method.getAnnotation(RedisLimit.class);

        if (redisLimit != null) {
            // 获取 redis 的 key
            String key = redisLimit.key();
            String className = method.getDeclaringClass().getName();
            String name = method.getName();
            String limitKey = key + className + name;
            log.info("限流的key:{}", limitKey);

            if (StringUtils.isEmpty(key)) {
                // 这里是自定义异常,为了方便写成了 RuntimeException
                throw new RuntimeException("code:101,msg:接口中 key 参数不能为空");
            }

            long limit = redisLimit.permitsPerSecond();
            long expire = redisLimit.expire();
            // 把 key 放入 List 中
            List<String> keys = new ArrayList<>(Collections.singletonList(key));
            Long count = stringRedisTemplate.execute(redisScript, keys, String.valueOf(limit), String.valueOf(expire));
            log.info("Access try count is {} for key ={}", count, keys);

            if (count != null && count == 0) {
                log.debug("令牌桶={}, 获取令牌失效,接口触发限流", key);
                throw new RuntimeException("code:10X, redisLimit.msg()");
            }
        }
    }
}

lua脚本代码

注意:脚本代码是放在 resources 文件下的,它的类型是 txt,名称后缀是lua。如果你不想改名称,就使用我写好的全名--> rateLimiter.lua

点击查看脚本代码
--获取KEY
local key = KEYS[1]

local limit = tonumber(ARGV[1])

local curentLimit = tonumber(redis.call('get', key) or "0")

if curentLimit + 1 > limit
    then return 0
else
    -- 自增长 1
    redis.call('INCRBY', key, 1)
    -- 设置过期时间
    redis.call('EXPIRE', key, ARGV[2])
    return curentLimit + 1
end

最后为了照顾纯小白,给大家看一下我的目录结构

使用 redis 实现分布式接口限流注解 RedisLimit文章来源地址https://www.toymoban.com/news/detail-712235.html

到了这里,关于使用 redis 实现分布式接口限流注解 RedisLimit的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分布式限流:Redis

    目录 1:如何实现分布式限流 2:限流的几种类别  2.1:固定窗口限流 2.2:滑动窗口限流 2.3:漏桶限流 2.4:令牌桶限流 3:实现分布式限流:Redis 3.1:引入Redisson的依赖包 3.2:初始化Redisson 3.3:创建Redisson的限流类  1:把统计用户的使用频率等这些数据放到一个集中的存储,比如redis,这样无

    2024年02月08日
    浏览(44)
  • 基于Redis的分布式限流详解

    Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案。 用最简单的话来说: 外部请求是不可控的,而我们系统的负载是有限的,如果没有限流机制,一旦外部请求超过系统承载

    2024年02月04日
    浏览(53)
  • 使用策略模式实现 Spring 分布式和单机限流

    我们可以使用策略模式来统一单机限流和分布式限流的实现,提高代码的可扩展性和可维护性。 思路是定义一个 RateLimitStrategy 接口,并分别实现单机限流策略 LocalRateLimitStrategy 和分布式限流策略 DistributedRateLimitStrategy 。在 AOP 切面中,根据配置决定使用哪种限流策略。 定义策略

    2024年04月24日
    浏览(40)
  • Redis的4种分布式限流算法

    服务系统流量多,的确是一件好事,但是如果过载,把系统打挂了,那大家都要吃席了。 所以,在各种大促活动之前,要对系统进行压测,评估整个系统的峰值QPS,要做一些限流的设置,超过一定阈值,就拒绝处理或者延后处理,避免把系统打挂的情况出现。 限流和熔断有

    2024年02月15日
    浏览(43)
  • 使用Redission自定义注解实现分布式锁(声明式)

    1.主要依赖 配置redission 自定义注解 sepl工具类 aop

    2024年02月11日
    浏览(45)
  • 分布式限流方案及实现

    优质博文:IT-BLOG-CN 限流是对高并发访问进行限制,限速的过程。通过限流来限制资源,可以提高系统的稳定性和可靠性,控制系统的负载,削峰填谷,保证服务质量。 服务限流后的常见处理方式: 【1】拒绝服务; 【2】排队或等待; 【3】服务降级(当服务器压力剧增的情

    2024年02月14日
    浏览(32)
  • 四种常见分布式限流算法实现!

    大家好,我是老三,最近公司在搞年终大促,随着各种营销活动“组合拳”打出,进站流量时不时会有一个小波峰,一般情况下,当然是流量越多越好,前提是系统能杠地住。大家都知道,一个分布式系统,有两个“弃车保帅”的策略: 限流 和 熔断 ,这期,我们就来讨论一

    2024年02月16日
    浏览(36)
  • 使用redis实现分布式锁

    在一个分布式系统中,也会涉及多个节点访问同一个公共资源的情况,此时就需要通过锁来做互斥控制,避免出现类似于“线程安全”的问题,而java的synchronized这样的锁只能在当前进程中生效,在分布式的这种多个进程多个主机的场景无能为力,此时就需要分布式锁。 例如

    2024年02月07日
    浏览(53)
  • 如何使用 Redis 快速实现分布式锁?

    本文我们来讨论如何使用 Redis 快速实现分布式锁。 分布式锁有很多种解决方案,前面简单介绍过,Redis 可以通过 set key 方式来实现分布式锁,但实际情况要更加复杂,比如如何确保临界资源的串行执行,如何及时释放,都是需要额外考虑的。 本文要讲的是一个完备的分布式

    2024年02月04日
    浏览(37)
  • SpringBoot使用Redis实现分布式缓存

    ✅作者简介:2022年 博客新星 第八 。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:SpringBoot 框架从入门到精通 ✨特色专栏:国学周更-心性养成之路 🥭本文内容:SpringBoot使用

    2023年04月09日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包