使用Google guava基于令牌桶实现限流

令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.使用Google guava基于令牌桶实现限流_第1张图片

使用guava实现限流

pom依赖
多加如下依赖

 
            com.google.guava
            guava
            23.0
        

yml 去掉数据库配置

AbstractInterceptor

 public abstract class AbstractInterceptor extends HandlerInterceptorAdapter {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            return preHandle(request);
        }
        protected abstract boolean preHandle(HttpServletRequest request);
    }

RateLimitInterceptor

@Component
public class RateLimitInterceptor extends AbstractInterceptor {
    public static final int REQUEST_COUNT = 1;
    /*** set the number of requests per second */
    private static final RateLimiter rateLimiter = RateLimiter.create(REQUEST_COUNT);

    @Override
    protected boolean preHandle(HttpServletRequest request) {
        if (!rateLimiter.tryAcquire()) {
            System.out.println(">>>>>>>>>> 亲!请稍后重试!");
            return false;
        }
        System.out.println(">>>>>>>> 恭喜您下单成功!");
        return true;
    }
}

WebMvcConfig

/**
 * @author administrator
 * extends WebMvcConfigurationSupport or implements WebMvcConfigurer
 */
@Component
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private RateLimitInterceptor rateLimitInterceptor;

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

DemoTestController

@RestController
public class DemoTestController {
    public static final String SUCCESS = "ok";
    @RequestMapping("/demoTest")
    public String demoTest() {
        return SUCCESS;
    }
}

jmeter测试

jmeter使用
使用Google guava基于令牌桶实现限流_第2张图片

测试效果

本人执行了三次
使用Google guava基于令牌桶实现限流_第3张图片

guava基本方法详解

以下许可的意思为token即令牌的意思

double acquire() 从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求,返回0表示获取令牌成功,否则阻塞等待获取


double acquire(int permits)从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求


static RateLimiter create(double permitsPerSecond)根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)


static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)根据指定的稳定吞吐率和预热期来创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少个请求量),在这段预热时间内,RateLimiter每秒分配的许可数会平稳地增长直到预热期结束时达到其最大速率。(只要存在足够请求数来使其饱和)


double getRate()返回RateLimiter 配置中的稳定速率,该速率单位是每秒多少许可数


void setRate(double permitsPerSecond)更新RateLimite的稳定速率,参数permitsPerSecond 由构造RateLimiter的工厂方法提供。


String toString()返回对象的字符表现形式


boolean tryAcquire()从RateLimiter 获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话,返回true表示获取成功,否则等待获取


boolean tryAcquire(int permits)从RateLimiter 获取许可数,如果该许可数可以在无延迟下的情况下立即获取得到的话


boolean tryAcquire(int permits, long timeout, TimeUnit unit)从RateLimiter 获取指定许可数如果该许可数可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可数的话,那么立即返回false (无需等待)


boolean  tryAcquire(long timeout, TimeUnit unit)从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)

其他限流解决方案

1、redis incr加过期时间来限流。

int current = jedis.incr(key);

if (current + 1 > limit) //如果超出限流大小

return 0;

else if (current == 1) //只有第一次访问需要设置2秒的过期时间

jedis.expire(key, “2”);

return 1

2、另外还有通过redis+lua来实现限流。

3、hystrix的线程池就类似漏桶的思路。

你可能感兴趣的:(算法系列)