限流(guava)

前言

只适用于单体应用

1. 依赖

   <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>

2. 测试

代码中只设置了每秒放一个令牌到,令牌桶

  1. acquire()
    该方法获取令牌,如果你没获取到,就会一直等待,不会丢失请求。
  2. tryAcquire()
    他首先会尝试获取令牌,如果拿到了返回true,没拿到返回false。他在设定的时间内去拿。
package com.jm.study.controller;

import com.google.common.util.concurrent.RateLimiter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class OrderController {

    RateLimiter rateLimiter = RateLimiter.create(1.0);

    @RequestMapping("/order")
    public String order(){
        System.out.println("生成令牌等待时间:" + rateLimiter.acquire());
        boolean acquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (!acquire) {
            System.out.println("你在怎么抢,也抢不到,因为会一直等待的,你先放弃吧!");
            return "你在怎么抢,也抢不到,因为会一直等待的,你先放弃吧!";
        }
        return "恭喜您,抢购成功!";
    }

    @RequestMapping("/order1")
    public String order1(){
        System.out.println("生成令牌等待时间:" + rateLimiter.acquire());
        return "恭喜您,抢购成功!";
    }

    @RequestMapping("/test")
    public void test(){
        for(int i = 0;i<10;i++){
            order1();
        }
    }
}

3. 注解版

tryAcquire 默认为false, 就是我们执行 rateLimiter.acquire() 这个方法,他不会丢失请求,
当tryAcquire为true的时候此时timeout参数生效,timeout为超时等待时间,如果等了该时间还没有获取到锁就报错,此时执行rateLimiter.tryAcquire()这个方法,该方法如果获取不到令牌就会导致请求丢失。
在使用过程中根据自己的实际情况使用

package com.jm.study.annotation;

import java.lang.annotation.*;

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRateLimiter {
    //向令牌桶放入令牌的速率
      double rate() default CommonRate.limitRate;

    boolean tryAcquire() default false;

    //从令牌桶获取令牌的超时时间
    long timeout() default 0;
}
package com.jm.study.aspect;

import com.google.common.util.concurrent.RateLimiter;
import com.jm.study.annotation.MyRateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Configuration
public class MyRateLimiterAspect {
    private RateLimiter rateLimiter = RateLimiter.create(2);
 
    @Pointcut("@annotation(com.jm.study.annotation.MyRateLimiter)")
    public void pointcut() {
    }
 
    /*** 核心切面方法 */
    @Around("pointcut()")
    public Object process(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        //使用反射获取方法上是否存在@MyRateLimiter注解
        MyRateLimiter myRateLimiter = signature.getMethod().getDeclaredAnnotation(MyRateLimiter.class);
        if (myRateLimiter == null) {
            //程序正常执行,执行目标方法
            return proceedingJoinPoint.proceed();
        }
        //获取注解上的参数
        // 获取配置的速率
        double rate = myRateLimiter.rate();
        //获取客户端等待令牌的时间
        long timeout = myRateLimiter.timeout();
        //设置限流速率
        rateLimiter.setRate(rate);
        //判断客户端获取令牌是否超时
        if(myRateLimiter.tryAcquire()){
            boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
            if (!tryAcquire) {
                //服务降级
                fullback();
                return null;
            }
        }else {
            rateLimiter.acquire();
        }
        //获取到令牌,直接执行
        return proceedingJoinPoint.proceed();
    }
 
    /*** 降级处理 */
    private void fullback() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response=attributes.getResponse();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.println("当前访问人数过多,请稍后再试!");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
  @RequestMapping("/order1")
    @MyRateLimiter(rate = 2.0)
    public String order1(){
        System.out.println("生成令牌等待时间:" + rateLimiter.acquire());
        return "恭喜您,抢购成功!";
    }
    
@RequestMapping("/order2")
    @MyRateLimiter(rate = 1.0, tryAcquire = true, timeout = 500)
    public String order2(){
        System.out.println("order2");
        return "order2";
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值