首页 > 其他分享 >使用Guava实现单体应用限流

使用Guava实现单体应用限流

时间:2023-09-27 11:59:29浏览次数:28  
标签:令牌 单体 public 限流 limit 注解 Guava id

一、概述

  服务器流量控制一直都是一个非常重要的问题。因为服务器是有性能瓶颈的,所以后台的接口也有其性能瓶颈,当辛辛苦苦的把多级缓存做好后,觉得可以承受高并发了的时候,服务突然就蹦了,可能是缓存爆掉了,也可能是数据库宕机了。造成这些问题的大多数原因就是流量太高了的问题。当然我们也可以进行服务的分布式部署。但今天不讨论这个,就讨论单体应用。

  单体应用的限流算法还是比较多的。

  1.AtomicInteger自己手动实现

  2.漏铜算法

  3.令牌算法

  今天就介绍下令牌算法,而且使用Guava框架自带的。

  原理如下:

系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,
则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。
令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。

 

二、示例(两种方法实现,其一是原生,其二是使用注解帮我们简化)

  1.引入guava和apo

   <!--本地缓存for guava cache-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>24.0-jre</version>

 <!--        面向切面编程-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

  2.使用原生实现

@Slf4j
@RestController
@RequestMapping("/api/v1/pub/limit/")
public class GuavaCurrentLimitingController {
    /**
     * 限流策略:1秒钟 2个请求(这里表示这个接口1秒钟服务器最多接收两个并发,其他请求直接返回人说过多的提示。)
     */
    private final RateLimiter limiter = RateLimiter.create(2.0);
    private DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @GetMapping("/test")
    public void test() {
        //500毫秒内没拿到令牌就进行服务降级
        boolean tryAcquire = limiter.tryAcquire(500, TimeUnit.MICROSECONDS);
        //如果没拿到令牌就进行服务降级
        if (!tryAcquire) {
            log.info("当前排队的人数过多,请稍后再试{}", LocalDateTime.now().format(dtf));
        }

        log.info("请求成功{}", LocalDateTime.now().format(dtf));
    }
}

  上述代码可以解决单体应用的简单限流问题。但是代码写的不够优雅。

  下面借助AOP+注解的形式对代码进行优化

  1.定义个Limit注解

/**
 * Guava自定义限流注解
 */
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target({ElementType.METHOD})//注解用在方法上
@Documented
public @interface Limit {
    /**
     * 唯一id
     * 作用:不同的接口执行不同的限流策略
     */
    String id() default "";

    /**
     * 最多访问次数限制
     */
    double permitsPerSecond();

    /**
     * 获取令牌最大等待时间
     */
    long timeout();

    /**
     * 获取令牌最大等待时间单位(默认毫秒)
     */
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;

    /**
     * 无法获取令牌的提示语
     */
    String errMsg() default "当前请求人数过多,请稍后再试";


}

  2.定义一个AOP类,来解析这个注解

/**
 * 使用自定义guava限流注解
 *
 * @author Tony
 * @version 2023
 * @date 2023/9/27 10:56
 */
@Slf4j
@Aspect
@Component
public class GuavaLimit {
    /**
     * 存储不同接口不同限流策略的map
     */
    private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Around("@annotation(com.tony.cursor.limit.Limit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //拿limit的注解
        Limit limit = method.getAnnotation(Limit.class);
        if (limit != null) {
            //id作用:不同的接口,不同的流量控制
            String id = limit.id();
            RateLimiter rateLimiter = null;
            //验证缓存是否有命中key
            if (!limitMap.containsKey(id)) {
                // 创建令牌桶
                rateLimiter = RateLimiter.create(limit.permitsPerSecond());
                limitMap.put(id, rateLimiter);
                log.info("新建了令牌桶={},容量={}", id, limit.permitsPerSecond());
            }
            rateLimiter = limitMap.get(id);
            // 拿令牌
            boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());
            // 拿不到命令,直接返回异常提示
            if (!acquire) {
                log.info("令牌桶={},获取令牌失败", id);
                throw new CustomException(limit.errMsg());//如果没有拿到令牌就直接抛异常
            }
        }
        return joinPoint.proceed();
    }

}

  3.使用注解进行限流

@Slf4j
@RestController
@RequestMapping("/api/v1/pub/limit/")
public class GuavaCurrentLimitingController {

    @GetMapping("/test2")
    @Limit(id = "limit2", permitsPerSecond = 1, timeout = 500, timeunit = TimeUnit.MILLISECONDS,
            errMsg = "当前排队人数过多,请稍后再试")
    public String test2() {
        log.info("拿到令牌请求成功");
        return "已拿到令牌,请求成功";
    }
}

  好了,优化完毕,测试结果如下所示:

 

标签:令牌,单体,public,限流,limit,注解,Guava,id
From: https://www.cnblogs.com/tony-yang-flutter/p/17732343.html

相关文章

  • .net webapi限流记录
     一,WebApiThrottleASP.NETWebAPIratelimiterforIISandOwinhostinghttps://github.com/stefanprodan/WebApiThrottle 二,AspNetCoreRateLimitASP.NETCoreratelimitingmiddlewarehttps://github.com/stefanprodan/AspNetCoreRateLimit......
  • 对某个接口进行限流 以 Aop 注解的形式绑定接口 用redis实现
    简单的针对某个接口进行限流,如果需要整体限流的话还是建议在网关上面或者服务器上面动手Controller:@LimitRequest(count=1,time=60*1000*2)@PostMapping("limit")publicStringgetLimitResult(){return"ok";}Annotation:@Retention(R......
  • Go语言实现接口IP限流,黑名单&白名单的实例,都可用!
    Go语言实现接口IP限流,黑名单&白名单的实例,都可用!原创 学习与分享 Go语言圈 2023-07-1808:30 发表于广东收录于合集#学Go语言哪些事儿221个MySQL大牛带你全面剖析与系统梳理数据库(mysql等)知识分享,总结数据库技巧和方法,提升你的技术技能。45篇原创内容......
  • 架构师面试必备:高并发限流算法全攻略
    Hello大家好,我是小米!今天我要和大家聊一聊一个在技术面试中经常被问到的问题——高并发限流算法!这个话题非常有趣,也是我们在日常工作中经常会碰到的挑战之一。在本文中,我将详细介绍一些常见的高并发限流算法,以及它们适用的不同场景。什么是高并发限流在开始探讨高并发限流算法之前,......
  • 使用 redis 实现分布式接口限流注解 RedisLimit
    前言很多时候,由于种种不可描述的原因,我们需要针对单个接口实现接口限流,防止访问次数过于频繁。这里就用redis+aop实现一个限流接口注解@RedisLimit代码点击查看RedisLimit注解代码importjava.lang.annotation.*;/***功能:分布式接口限流注解*@authorlove......
  • Guava Retry
    定义:一个重试机制的框架。使用方式:添加依赖<dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version></dependency>创建重试器,执行重试方法......
  • Nginx 限流及WAF
    Nginx提供了两种限流手段:一是控制速率,二是控制并发连接数。控制速率是按:limit_req_zone来限制单位时间内容的请求书,即速率限制。控制并发数:利用limit_conn_zone和limit_conn两个指令来控制并发数.1.ngx_http_limit_conn_modulengx_http_limit_conn_modul:基于key($binary_re......
  • SpringBoot单体用户登录校验
    一、概述要做一个有私有空间的单体的SpringBoot项目,用户的权限校验是必须得。需要指定哪些接口需要权限才能访问,哪些接口不需要权限就能访问。目标:1.用户登录、注册不需要权限校验,获取用户信息需要权限校验2.获取用户信息通过token来获取(从token中取......
  • 限流方案
    dg-publish:truetitle:安全:如何设计高吞吐和大流量分布式集群的限流方案?createTime:2023-09-1223:19tags:-kafka-rocketqm安全:如何设计高吞吐和大流量分布式集群的限流方案?消息队列系统安全的第二部分:数据自我保护和服务自我保护。数据维度的自我保护主要指如......
  • 关于熔断、限流、降级
    慢慢补充熔断、限流、降级熔断:根据(策略、手动)当多次请求失败的时候进行熔断,直接返回错误信息,防止大量线程聚集等待。限流:分为QPS(每秒请求数)、CPS(每秒并发数)限流,当qps、cps数量超过限制,直接返回错误信息,防止外部请求打崩系统。降级:八股文先生表示:像淘宝双十一,对一些不必要的......