中小型项目请求限流设计

这篇具有很好参考价值的文章主要介绍了中小型项目请求限流设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

何为请求限流?

请求限流是一种控制API或其他Web服务的流量的技术。它的目的是限制客户端对服务器发出的请求的数量或速率,以防止服务器过载或响应时间变慢,从而提高系统的可用性和稳定性。

中小型项目请求限流的需求

  1. 按IP、用户、全局限流
  2. 基于不同实现的限流设计(基于Redis或者LRU缓存)
  3. 基于注解标注哪些接口限流

完整限流设计实现在开源项目中:https://github.com/valarchie/AgileBoot-Back-End

注解设计

声明一个注解类,主要有以下几个属性

  • key(缓存的key)
  • time(时间范围)
  • maxCount(时间范围内最大的请求次数)
  • limitType(按IP/用户/全局进行限流)
  • cacheType(基于Redis或者Map来实现限流)
/**
 * 限流注解
 *
 * @author valarchie
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    /**
     * 限流key
     */
    String key() default "None";

    /**
     * 限流时间,单位秒
     */
    int time() default 60;

    /**
     * 限流次数
     */
    int maxCount() default 100;

    /**
     * 限流条件类型
     */
    LimitType limitType() default LimitType.GLOBAL;

    /**
     * 限流使用的缓存类型
     */
    CacheType cacheType() default CacheType.REDIS;

}

LimitType枚举,我们可以将不同限制类型的逻辑直接放在枚举类当中。推荐将逻辑直接放置在枚举类中,代码的组织形式会更好。

enum LimitType {
        /**
         * 默认策略全局限流  不区分IP和用户
         */
        GLOBAL{
            @Override
            public String generateCombinedKey(RateLimit rateLimiter) {
                return rateLimiter.key() + this.name();
            }
        },

        /**
         * 根据请求者IP进行限流
         */
        IP {
            @Override
            public String generateCombinedKey(RateLimit rateLimiter) {
                String clientIP = ServletUtil.getClientIP(ServletHolderUtil.getRequest());
                return rateLimiter.key() + clientIP;
            }
        },

        /**
         * 按用户限流
         */
        USER {
            @Override
            public String generateCombinedKey(RateLimit rateLimiter) {
                LoginUser loginUser = AuthenticationUtils.getLoginUser();
                if (loginUser == null) {
                    throw new ApiException(ErrorCode.Client.COMMON_NO_AUTHORIZATION);
                }
                return rateLimiter.key() + loginUser.getUsername();
            }
        };

        public abstract String generateCombinedKey(RateLimit rateLimiter);

    }
    

CacheType, 主要分为Redis和Map, 后续有新的类型可以新增。


  enum CacheType {

      /**
       * 使用redis做缓存
       */
      REDIS,

      /**
       * 使用map做缓存
       */
      Map

  }


RateLimitChecker设计

声明一个抽象类,然后将具体实现放在实现类中,便于扩展

/**
 * @author valarchie
 */
public abstract class AbstractRateLimitChecker {

    /**
     * 检查是否超出限流
     * @param rateLimiter
     */
    public abstract void check(RateLimit rateLimiter);

}

Redis限流实现

/**
 * @author valarchie
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class RedisRateLimitChecker extends AbstractRateLimitChecker{

    @NonNull
    private RedisTemplate<Object, Object> redisTemplate;

    private final RedisScript<Long> limitScript = new DefaultRedisScript<>(limitScriptText(), Long.class);

    @Override
    public void check(RateLimit rateLimiter) {
        int maxCount = rateLimiter.maxCount();
        String combineKey = rateLimiter.limitType().generateCombinedKey(rateLimiter);

        Long currentCount;
        try {
            currentCount = redisTemplate.execute(limitScript, ListUtil.of(combineKey), maxCount, rateLimiter.time());
            log.info("限制请求:{}, 当前请求次数:{}, 缓存key:{}", combineKey, currentCount, rateLimiter.key());
        } catch (Exception e) {
            throw new RuntimeException("redis限流器异常,请确保redis启动正常");
        }

        if (currentCount == null) {
            throw new RuntimeException("redis限流器异常,请稍后再试");
        }

        if (currentCount.intValue() > maxCount) {
            throw new ApiException(ErrorCode.Client.COMMON_REQUEST_TOO_OFTEN);
        }

    }

    /**
     * 限流脚本
     */
    private static String limitScriptText() {
        return "local key = KEYS[1]\n" +
            "local count = tonumber(ARGV[1])\n" +
            "local time = tonumber(ARGV[2])\n" +
            "local current = redis.call('get', key);\n" +
            "if current and tonumber(current) > count then\n" +
            "    return tonumber(current);\n" +
            "end\n" +
            "current = redis.call('incr', key)\n" +
            "if tonumber(current) == 1 then\n" +
            "    redis.call('expire', key, time)\n" +
            "end\n" +
            "return tonumber(current);";
    }

}

Map + Guava RateLimiter实现

/**
 * @author valarchie
 */
@SuppressWarnings("UnstableApiUsage")
@Component
@RequiredArgsConstructor
@Slf4j
public class MapRateLimitChecker extends AbstractRateLimitChecker{

    /**
     * 最大仅支持4096个key   超出这个key  限流将可能失效
     */
    private final LRUCache<String, RateLimiter> cache = new LRUCache<>(4096);


    @Override
    public void check(RateLimit rateLimit) {
        String combinedKey = rateLimit.limitType().generateCombinedKey(rateLimit);

        RateLimiter rateLimiter = cache.get(combinedKey,
            () -> RateLimiter.create((double) rateLimit.maxCount() / rateLimit.time())
        );

        if (!rateLimiter.tryAcquire()) {
            throw new ApiException(ErrorCode.Client.COMMON_REQUEST_TOO_OFTEN);
        }

        log.info("限制请求key:{}, combined key:{}", rateLimit.key(), combinedKey);
    }

}

限流切面

我们需要在切面中,读取限流注解标注的信息,然后选择不同的限流实现来进行限流。

/**
 * 限流切面处理
 *
 * @author valarchie
 */
@Aspect
@Component
@Slf4j
@ConditionalOnExpression("'${agileboot.embedded.redis}' != 'true'")
@RequiredArgsConstructor
public class RateLimiterAspect {

    @NonNull
    private RedisRateLimitChecker redisRateLimitChecker;

    @NonNull
    private MapRateLimitChecker mapRateLimitChecker;


    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimit rateLimiter) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        log.info("当前限流方法:" + method.toGenericString());

        switch (rateLimiter.cacheType()) {
            case REDIS:
                redisRateLimitChecker.check(rateLimiter);
                break;
            case Map:
                mapRateLimitChecker.check(rateLimiter);
                return;
            default:
                redisRateLimitChecker.check(rateLimiter);
        }

    }

}

注解使用

以下是我们标注的注解例子。

time=10,maxCount=10表明10秒内最多10次请求。

cacheType=Redis表明使用Redis来实现。

limitType=IP表明基于IP来限流。

/**
 * 生成验证码
 */
@Operation(summary = "验证码")
@RateLimit(key = RateLimitKey.LOGIN_CAPTCHA_KEY, time = 10, maxCount = 10, cacheType = CacheType.REDIS,
    limitType = LimitType.IP)
@GetMapping("/captchaImage")
public ResponseDTO<CaptchaDTO> getCaptchaImg() {
    CaptchaDTO captchaImg = loginService.generateCaptchaImg();
    return ResponseDTO.ok(captchaImg);
}

这是笔者关于中小型项目关于请求限流的实现,如有不足欢迎大家评论指正。文章来源地址https://www.toymoban.com/news/detail-409888.html

全栈技术交流群:1398880

到了这里,关于中小型项目请求限流设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 基于SpringBoot的中小型超市数据分析系统设计与实现

    摘 要 I Abstract II 引 言 1 1 系统开发相关技术 3 1.1 SpringBoot框架 3 1.1.1发展历程 3 1.1.2 什么是SpringBoot 3 1.1.3 SpringBoot特性 3 1.1.4 SpringBoot的优势 3 1.2 MyBatis框架 4 1.2.1框架简介 4 1.2.2框架特性 4 1.3 Java语言 5 1.3.1 Java语言简介 5 1.3.2 Java语言的重要特性 5 1.4 BootStrap框架 6 1.4.1框架简介

    2024年03月08日
    浏览(42)
  • 【计算机三级网络技术】 第二篇 中小型系统总体规划与设计

    基于网络的信息系统结构应包括 网络运行环境、网络系统、网络操作系统。 网络运行环境指为了保障网络系统安全、可靠与正常运行所需的基本设施和设备条件,主要包括 机房与电源 两部分。 (1)机房和设备间、配线间。 机房用于放置交换机、核心路由器、服务器等核心

    2024年02月09日
    浏览(35)
  • 9.3K+ Star!一个面向中小型企业设计的开源运维平台!

    大家好,我是 Java陈序员 。 我们在日常开发中,会有很多的应用环境, 开发环境、测试环境、回归环境、生产环境 等等。 这些环境,需要部署在一台台的服务器上,有的可能是物理机,有的可能是云服务器。 那么,这么多主机我们要怎么运维整理呢? 今天,给大家介绍一

    2024年02月05日
    浏览(35)
  • Cisco Packet Trancer中小型校园网/企业网/园区网网络设计规划/无线网络

     有需求,见评论私信交流!!! 项目演示视频: Cisco PT软件模拟实现双核心中型企业/校园网 网络架构拓扑设计、论文,毕设_哔哩哔哩_bilibili 例1:       目录 摘要 一、 绪论 (一)项目背景分析 (二) 企业园区网发展现状 二、系统需求分析 (一)项目背景分析 (二)

    2024年02月09日
    浏览(39)
  • 中小型企业网络的组建

    某企业计划建设自己的企业园区网络,希望通过这个新建的网络提供一个安全、可靠、可扩展、高效的网络环境,将两个办公地点连接到一起,使企业内能够方便快捷地实现网络资源共享、全网接入Internet等目标,同时实现公司内部的信息保密隔离,以及对于公网的安全访问

    2024年02月08日
    浏览(85)
  • 中小型企业网网络搭建ensp模拟

    本期模拟中小型企业的万能组网,该场景为总部与分部之间的跨运营商互访,如果拆开来,就是小型企业的内网环境,技术可以任意搭配 场景1:总部部署STPRSTPVRRPOSPF静态,基于防火墙的GRE VPNIPSEC VPN、NAT 场景2:总部部署STPMSTPVRRP负载OSPF静态,基于防火墙的GRE VPNIPSEC

    2024年02月07日
    浏览(33)
  • ensp典型中小型企业网搭建(带无线)

    该设计规划的是一个公司的网络搭建,采用接入层、核心层、汇聚层三层网络。所有接入层汇聚层交换机运行MSTP和VRRP协议,做冗余备份,保护设备和链路稳定性。运行ospf动态路由协议,方便路由维护。使用dhcp动态分配地址,便于ip地址管理。出口采用防火墙设备,保护网络

    2024年02月05日
    浏览(37)
  • 企业知识库搭建全流程,中小型企业必看

    知识库是企业知识管理和信息查询的重要平台,对企业效率提升,业务流程规范和企业文化建设有着重要的影响。那么,如何为企业搭建一个合适,高效,易用的知识库呢?接下来就为中小型企业详解企业知识库搭建全流程。 1.明确知识库的目的和目标受众 首先,你需要明确

    2024年01月16日
    浏览(39)
  • ssm中小型企业财务管理系统源码和论文

    ssm中小型企业财务管理系统源码和论文067  开发工具:idea   数据库mysql5.7+  数据库链接工具:navcat,小海豚等   技术:ssm 1、研究目的意义 社会经济的迅速发展和科学技术的全面进步,计算机技术的飞速发展,以及计算机与通信技术为基础的信息系统正处于蓬勃发展的时期

    2024年02月11日
    浏览(46)
  • 无线AP中小型、大型两种常见组网方式

    无线AP(Access Point)网络覆盖是现代无线网络中的重要组成部分。它提供了无线信号的传输和接收功能,使用户能够在无线网络中进行通信和访问互联网。针对不同的需求和场景,存在两种常见的无线AP网络覆盖组网方式:中小型的无线覆盖组网方式和大范围的无线覆盖组网方

    2024年02月06日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包