当前位置: 首页 > article >正文

java Redisson 实现限流每秒/分钟/小时限制N个

1.引入maven包:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.1</version>
        </dependency>

2.代码实现:

a.RedissonConfig 初始化

package com.hanyc.demo.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * redisson
 *
 * @author hanyc
 * @date 2024/12/31 13:30
 * @company: 
 */
@Configuration
public class RedissonConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        if (redisProperties.getCluster() != null && !redisProperties.getCluster().getNodes().isEmpty()) {
            RedisProperties.Cluster cluster = redisProperties.getCluster();
            List<String> nodes = cluster.getNodes();
            List<String> newNodes = new ArrayList<>();
            nodes.forEach(index -> newNodes.add(
                    index.startsWith("redis://") ? index : "redis://" + index));
            config.useClusterServers()
                    .addNodeAddress(newNodes.toArray(new String[0]))
                    .setPassword(redisProperties.getPassword());
        } else {
            String address = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
            config.useSingleServer().setAddress(address).setPassword(redisProperties.getPassword());
        }
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}

b.UserRateLimiterInterceptor  用户请求限流拦截器

package com.hanyc.demo.config;

import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description: 用户请求限流拦截器
 * @Author hanyc
 * @Date 2024/12/12
 **/
@Component
@Slf4j
public class UserRateLimiterInterceptor implements HandlerInterceptor {

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 假设这个方法能够从请求中解析出用户ID
        String userIdStr = "";
        String userIp = ServletUtil.getClientIP(request);
        // 假设用户ID 为 1
        String userId = "1";
        if (userId == null) {
            // 所有未登录账号 不需要验证的请求,都使用userId=10000
            userIdStr = userIp;
        } else {
            userIdStr = userId.toString();
        }
        // 为每个用户生成唯一的限流键
        String rateLimiterKey = "USER_RATE_LIMITER" + userIdStr;
        // 删除 Redis 中存储的限流器状态(清除缓存)
        // RRateLimiter 在内部将配置保存在 Redis 中,并且这些配置是持久化的。删除该键后,Redis 会丢失之前存储的限流配置,从而确保新的配置能够被应用。
        redissonClient.getBucket(rateLimiterKey).delete();
        // 获取分布式的 RRateLimiter 实例
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(rateLimiterKey);
        // 初始化限流器,限制每两秒最多 10 次请求
        // 参数说明: 
        // RateType.OVERALL 全局
        // rate 时间限制内可以请求多少次
        // rateInterval 多少时间内限制
        // 时间单位 可以秒/分钟/小时等
        rateLimiter.trySetRate(RateType.OVERALL, 10, 2, RateIntervalUnit.SECONDS);
        log.info("当前路由为:{} userIdStr:{} userIp:{}", request.getRequestURI(), userIdStr, userIp);
        // 尝试获取令牌,如果获取不到说明超过请求限制
        if (rateLimiter.tryAcquire()) {
            // 允许继续处理请求
            return true;
        } else {
            // 如果获取不到令牌,则说明请求超过了限制,可以在这里抛出异常或者返回错误信息
            log.warn("当前异常的路由为:{} userIdStr:{} userIp :{}", request.getRequestURI(), userIdStr, userIp);
            return false;
        }
    }


}

c. WebMvcConfig 添加自定义拦截器

package com.hanyc.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


/**
 * MVC自定义配置
 *
 * @author hanyc
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Bean
    public UserRateLimiterInterceptor userRateLimiterInterceptor() {
        return new UserRateLimiterInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userRateLimiterInterceptor()).addPathPatterns("/**");
    }
}

3.测试

使用JMeter 发送请求. 每秒15个.

查看打印结果:

前10个请求正常响应. 后五个请求被拦截


http://www.kler.cn/a/464691.html

相关文章:

  • stm32入门元件介绍
  • 【NX入门篇】
  • 家教老师预约平台小程序系统开发方案
  • 【顶刊TPAMI 2025】多头编码(MHE)之Part 6:极限分类无需预处理
  • SD下载、安装、使用、卸载-Stable Diffusion整合包v4.10发布!
  • sklearn_pandas.DataFrameMapper的用法
  • 启智云脑Ascend调试平台安装gcc
  • Ubuntu执行sudo apt-get update失败的解决方法
  • Hive如何创建自定义函数(UDF)?
  • 25年1月更新。Windows 上搭建 Python 开发环境:Python + PyCharm 安装全攻略(文中有安装包不用官网下载)
  • Transformer入门指南:从原理到实践
  • 有哪几种方法可以使html脱离文档流?
  • 华为OD E卷(100分)44-单次接龙
  • 深入浅出 Beam Search:自然语言处理中的高效搜索利器
  • ThinkPHP 模板引擎使用技巧:提高开发效率
  • 第四讲 比特币的主流化与价格波动
  • [python SQLAlchemy数据库操作入门]-24.使用 Celery 与 SQLAlchemy:异步任务处理股票数据
  • 互慧-急诊综合管理平台 ServicePage.aspx 任意文件读取漏洞复现
  • Spring Security(maven项目) 3.0.2.3版本
  • Docker图形化界面工具Portainer最佳实践
  • Unix/Linux 系统中环境变量有哪些
  • 两个等号和三个等号(待查资料)
  • Centos7部署NTP服务及客户端同步实践
  • 使用 4 种主要方法将数据从 HTC 传输到华为
  • 解决Spring3.4版本中使用QueryDSL中出现MongoAnnotationProcessor使用问题
  • Javascript算法——回溯算法(组合问题)