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

yizhihongxing

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日

相关文章

  • Linux下Redis的安装和部署

    Linux下Redis的安装和部署 Redis是一个开源的内存数据结构存储系统,经常被用来做缓存、实时数据分析、消息队列、任务队列等。本文将介绍在Linux系统下安装和部署Redis的完整攻略。 安装Redis 下载Redis 在Redis的官网(https://redis.io/)上下载最新的Redis稳定版本。例如,我们选择下载Redis 6.2.4版本…

    database 2023年5月22日
    00
  • intro.js 页面引导简单用法 分享

    Intro.js 页面引导简单用法 简介 Intro.js 是一个轻量级的网页引导库,可以用来为用户展示页面功能、元素位置、操作流程等,能够帮助用户更好地理解网站的使用方法,提高用户的满意度。 安装 可以通过 npm 或 yarn 安装 intro.js: npm install intro.js –save # 或者 yarn add intro.js …

    database 2023年5月22日
    00
  • 解决django 向mysql中写入中文字符出错的问题

    确认数据库字符集 在使用Django向MySQL中写入中文字符时,需要先确认MySQL数据库的字符集是否为utf8或utf8mb4,这是因为MySQL默认字符集为latin1,不支持存储中文字符。可以通过以下操作来查看和修改: 查看数据库字符集: SHOW VARIABLES LIKE ‘character_set_database’; 修改数据库字符集: …

    database 2023年5月19日
    00
  • PouchDB 和 MongoDB 的区别

    PouchDB 和 MongoDB 都是流行的 NoSQL 数据库,但是它们有不同的用途和功能。下面我们详细讲解它们的区别。 1. 数据存储方式 MongoDB 是一个传统的服务器端数据库,它使用纯粹的基于磁盘的存储方式,即将数据写入硬盘中的文件中。MongoDB 核心的思想是将数据存储在集合(Collections)中,这些集合可以通过索引来查找。Mong…

    database 2023年3月27日
    00
  • 浅谈安装ORACLE时在Linux上设置内核参数的含义

    安装ORACLE时在Linux上设置内核参数是非常重要的,以便获得最佳的系统性能和数据安全性。接下来,我们将详细讲解如何在Linux上设置内核参数。 安装ORACLE前的准备工作 在安装ORACLE前,我们需要进行一些准备工作。 确认内核版本:使用uname -r命令查询系统内核版本。 确认内存大小:使用cat /proc/meminfo | grep Me…

    database 2023年5月22日
    00
  • Sql Server数据把列根据指定内容拆分数据的方法实例

    首先我们需要明确一下题目的意思。根据指定内容拆分数据,指的是将某一列中的数据按照指定的内容进行分割,然后将结果分别存储到新的列中。例如,我们想要将“姓名-年龄-性别”的格式拆分成“姓名”、“年龄”、“性别”三列数据,就可以使用下面的方法来实现。 下面是具体实现步骤: 1. 使用CHARINDEX函数获取分隔符位置 在SQL Server中,我们可以使用CHA…

    database 2023年5月22日
    00
  • Centos和Redhat的区别与联系

    CentOS和Red Hat的区别与联系 关于CentOS和Red Hat CentOS和Red Hat企业版(RHEL)都是企业级的Linux操作系统。Red Hat是由Red Hat公司开发和维护的商业操作系统,CentOS则是由社区开发和维护的免费、开源版本的RHEL。CentOS的开发目标是为了提供一个和RHEL一样稳定、可靠的操作系统,但完全免费,…

    database 2023年5月22日
    00
  • Linux运维MySQL必会面试题100道

    Linux运维MySQL必会面试题100道攻略 前言 MySQL是Linux系统下常见的关系型数据库管理系统之一,常用于Web应用程序的后端数据库。对于Linux运维人员来说,熟练掌握MySQL的使用和管理是非常重要的技能之一。在面试过程中,往往会考察一些MySQL相关的技术知识。本文汇总了100道MySQL面试题及其答案,旨在为Linux运维人员提供参考和…

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