Redis+AOP+自定义注解实现限流

Redis + AOP + 自定义注解实现限流的攻略分为以下几个步骤:

1. 集成 Redis

Redis 是一种基于内存的数据存储系统,它可以高效地存储和操作数据,特别适合用于缓存和限流等场景。我们首先需要将 Redis 集成到项目中。

可以使用官方的 Java 客户端 Jedis 来访问 Redis。在 Maven 中引入 Jedis 的依赖,并配置 Redis 的连接地址和密码:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.3</version>
</dependency>
@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("localhost");
        config.setPort(6379);
        config.setPassword("password");
        return new LettuceConnectionFactory(config);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory());
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }

}

2. 定义自定义注解

我们根据业务需求定义一个自定义注解 @RateLimit,该注解用于限制某个方法的访问频率。该注解包含三个参数:

  • value:限制的时间窗口,单位为秒。
  • limit:时间窗口内的最大访问次数。
  • key:访问次数计数器的键名,可以使用 SpEL 表达式动态生成。

我们在代码中使用 @RateLimit 注解来标记需要进行限流的方法。在 AOP 中,我们通过解析该注解来实现具体的限流逻辑。

/**
 * 限流注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    // 限制的时间窗口,单位为秒
    int value() default 1;

    // 时间窗口内的最大访问次数
    int limit() default 10;

    // 访问次数计数器的键名
    String key() default "";

}

3. 实现 AOP 拦截器

我们使用 AOP 拦截器来实现限流功能。在进入被拦截的方法之前,检查该方法是否被 @RateLimit 注解标记,如果是,则使用 Redis 实现计数器来限制访问频率。

在实现 AOP 拦截器时,我们需要使用 @Around 注解来标记拦截器方法,并使用 ProceedingJoinPoint 对象来访问拦截的方法和它的参数。在拦截器中,我们首先解析 @RateLimit 注解,并根据注解参数来确定时间窗口和访问次数限制。然后,我们通过 Redis 的 incr() 命令来对访问次数计数器进行累加,并使用 expire() 命令设置计数器的过期时间。最后,如果计数器的值超过了访问次数限制,我们就抛出 RateLimitException 异常。

/**
 * 限流拦截器
 */
@Aspect
@Component
public class RateLimitAspect {

    private static final Logger logger = LoggerFactory.getLogger(RateLimitAspect.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(rateLimit)")
    public Object limit(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        Signature signature = point.getSignature();
        String methodName = signature.getName();
        String className = signature.getDeclaringTypeName();
        Object[] args = point.getArgs();

        String key = rateLimit.key();
        if (StringUtils.isBlank(key)) {
            key = className + "#" + methodName;
        }

        int limit = rateLimit.limit();
        int value = rateLimit.value();
        long count = redisTemplate.opsForValue().increment(key, 1);
        redisTemplate.expire(key, value, TimeUnit.SECONDS);

        logger.debug("Request {}#{}, rate limit is {}/{}", className, methodName, count, limit);
        if (count > limit) {
            throw new RateLimitException("You have been rate-limited");
        }

        return point.proceed(args);
    }

}

4. 测试限流

我们通过两个示例来演示如何使用 @RateLimit 注解和限流拦截器来限制方法的访问频率。

首先,我们定义一个接口 HelloService,其中有两个方法 helloworld,分别被 @RateLimit 注解标记:

public interface HelloService {

    @RateLimit(value = 1, limit = 5)
    String hello(String name);

    @RateLimit(key = "#name", value = 1, limit = 2)
    String world(String name);

}

hello 方法有一个时间窗口为 1 秒,访问次数限制为 5 的限制;world 方法则根据参数动态生成计数器的键名,并设置时间窗口为 1 秒,访问次数限制为 2。

现在,我们使用 Spring Boot 来实现 HelloService 接口,并调用其中的两个方法:

@RestController
public class HelloController implements HelloService {

    @Override
    public String hello(String name) {
        return "Hello, " + name;
    }

    @Override
    public String world(String name) {
        return "World, " + name;
    }

}

我们使用 Postman 工具来模拟请求,并设置每秒钟发起 10 次请求。在限流功能生效之前,我们可以得到 10 次正常响应;在限流功能生效之后,我们可以看到只有前 5 次请求得到了响应,后 5 次请求因为超过了访问次数限制而得到响应码为 429 的错误响应。

下面是两个示例的详细代码:

示例 1:固定时间窗口限流

@RestController
public class FixedWindowController {

    @RateLimit(value = 1, limit = 5)
    @GetMapping("/fixed")
    public String fixedWindow() {
        return "Success";
    }

}

示例 2:滑动时间窗口限流

@RestController
public class SlidingWindowController {

    @RateLimit(key = "#name", value = 1, limit = 2)
    @GetMapping("/sliding/{name}")
    public String slidingWindow(@PathVariable String name) {
        return "Hello, " + name;
    }

}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Redis+AOP+自定义注解实现限流 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • Mysql IN语句查询

    语法: WHERE column IN (value1,value2,…) WHERE column NOT IN (value1,value2,…) 1、in 后面是记录集,如: select * from table where uname in(select uname from user); 例子: SELECT * FROM article…

    MySQL 2023年4月13日
    00
  • SQL 联合查询与XML解析实例详解

    SQL 联合查询与 XML 解析实例详解 背景 SQL(Structured Query Language,结构化查询语言)是用于管理关系数据库管理系统的标准语言。XML(eXtensible Markup Language,可扩展标记语言)则是一种用于存储和传输数据的语言。本文将详细讲解 SQL 联合查询与 XML 解析的实例,以帮助读者更深入地理解这两个…

    database 2023年5月22日
    00
  • MySQL之复杂查询的实现

    MySQL之复杂查询的实现完整攻略 MySQL可以通过使用复杂查询语句对多个表进行连接、筛选、排序等操作,以得到需要的结果。在进行复杂查询时,需要注意以下几点: 联结多个表时,需要指定表之间的关系。 一般采用JOIN关键字。例如: sql SELECT * FROM customers JOIN orders ON customers.customerID …

    database 2023年5月22日
    00
  • Mysql联合查询UNION和Order by同时使用报错问题的解决办法

    当在MySQL中使用联合查询UNION以及Order by排序时,可能会遇到以下错误: #1221 – Incorrect usage of UNION and ORDER BY 这是因为UNION和Order by语句是有限制的,不能在同一个查询中同时使用。不过这个问题可以通过以下两种方式来解决: 解决方法一:将UNION查询结果作为子查询进行排序 这种方…

    database 2023年5月22日
    00
  • ASP常用函数收藏乱七八糟未整理版

    ASP常用函数收藏乱七八糟未整理版 总览 本攻略旨在整理ASP中经常使用的函数,让读者们可以快速了解和掌握这些函数的使用方法。 以下是本攻略涉及到的函数列表: Len() LCase() UCase() Left() Right() Mid() Replace() Trim() FormatCurrency() FormatDateTime() Format…

    database 2023年5月22日
    00
  • 一千行的MySQL学习笔记汇总

    一千行的MySQL学习笔记汇总是一个非常全面的MySQL学习资源,旨在帮助初学者快速入门和深入理解MySQL数据库。接下来,我将为您介绍如何使用这个资源,包括获取和使用该笔记的步骤。 获取一千行的MySQL学习笔记汇总 打开GitHub,搜索“一千行的MySQL学习笔记汇总”或者直接访问https://github.com/it-interview/Easy…

    database 2023年5月22日
    00
  • 浅谈MySQL和mariadb区别

    浅谈MySQL和mariadb区别 介绍 MySQL和mariadb都是关系型数据库管理系统。他们都可以提供安全性和可靠性的数据存储,但是他们也有一些显著的区别。这篇文章将会从多个方面对MySQL和mariadb进行比较。 开发商 MySQL最初是由MySQL AB公司开发并持有版权,这个公司被 Sun 公司收购后,又被 Oracle 公司收购。而maria…

    database 2023年5月21日
    00
  • HTTP 错误 500.19- Internal Server Error 错误解决方法

    HTTP 错误 500.19- Internal Server Error 是一种常见的服务器错误,在开发和管理网站时可能会遇到。该错误通常表示 IIS 配置文件中有一些问题,服务器无法对请求做出正确响应导致的。以下是解决该问题的完整攻略: 1. 确定错误类型 首先,需要确定出现的 500.19 错误的具体类型,以便知道该如何修复它。主要有三种类型: 错误代…

    database 2023年5月21日
    00
合作推广
合作推广
分享本页
返回顶部