一.项目背景

最近项目中需要进行接口保护,防止高并发的情况把系统搞崩,因此需要对一个查询接口进行限流,主要的目的就是限制单位时间内请求此查询的次数,例如1000次,来保护接口。

参考了 开涛的博客聊聊高并发系统限流特技 ,学习了其中利用Google Guava缓存实现限流的技巧,在网上也查到了很多关于Google Guava缓存的博客,学到了好多,推荐一个博客文章:http://ifeve.com/google-guava-cachesexplained/,关于Google Guava缓存的更多细节或者技术,这篇文章讲的很详细;

这里我们并不是用缓存来优化查询,而是利用缓存,存储一个计数器,然后用这个计数器来实现限流。

二.效果实验

static LoadingCache<Long, AtomicLong> count = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoader<Long, AtomicLong>() {
@Override
public AtomicLong load(Long o) throws Exception {
//System.out.println("Load call!");
return new AtomicLong(0L);
}
});

上面,我们通过CacheBuilder来新建一个LoadingCache缓存对象count,然后设置其有效时间为两秒,即每两秒钟刷新一次;缓存中,key为一个long型的时间戳类型,value是一个计数器,使用原子性的AtomicLong保证自增和自减操作的原子性, 每次查询缓存时如果不能命中,即查询的时间戳不在缓存中,则重新加载缓存,执行load将当前的时间戳的计数值初始化为0。这样对于每一秒的时间戳,能计算这一秒内执行的次数,从而达到限流的目的;

这是要执行的一个getCounter方法:

public class Counter {
static int counter = 0;
public static int getCounter() throws Exception{
return counter++;
}
}

现在我们创建多个线程来执行这个方法:

ublic class Test {

    public static void main(String args[]) throws Exception
{
for(int i = 0;i<100;i++)
{
new Thread(){
@Override
public void run() {
try {
System.out.println(Counter.getCounter());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
} }
}

这样执行的话,执行结果很简单,就是很快地执行这个for循环,迅速打印0到99折100个数,不再贴出。

这里的for循环执行100个进程时间是很快的,那么现在我们要限制每秒只能有10个线程来执行getCounter()方法,该怎么办呢,上面讲的限流方法就派上用场了:

public class Counter {
static LoadingCache<Long, AtomicLong> count = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoader<Long, AtomicLong>() {
@Override
public AtomicLong load(Long o) throws Exception {
System.out.println("Load call!");
return new AtomicLong(0L);
}
});
static long limits = 10;
static int counter = 0;
public static synchronized int getCounter() throws Exception{
while (true)
{
//获取当前的时间戳作为key
Long currentSeconds = System.currentTimeMillis() / 1000;
if (count.get(currentSeconds).getAndIncrement() > limits) {
continue;
}
return counter++;
}
}
}

这样一来,就可以限制每秒的执行数了。对于每个线程,获取当前时间戳,如果当前时间(当前这1秒)内有超过10个线程正在执行,那么这个进程一直在这里循环,直到下一秒,或者更靠后的时间,重新加载,执行load,将新的时间戳的计数值重新为0。

执行结果:



每秒执行11个(因为从0开始),每一秒之后,load方法会执行一次;

为了更加直观,我们可以让每个for循环sleep一段时间:

public class Test {

    public static void main(String args[]) throws Exception
{
for(int i = 0;i<100;i++)
{
new Thread(){
@Override
public void run() {
try {
System.out.println(Counter.getCounter());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
Thread.sleep(100);
} }
}

在上述这样的情况下,一个线程如果遇到当前时间正在执行的线程超过limit值就会一直在while循环,这样会浪费大量的资源,我们在做限流的时候,如果出现这种情况,可以不进行while循环,而是直接抛出异常或者返回,来拒绝这次执行(查询),这样便可以节省资源。

Google Guava缓存实现接口的限流的更多相关文章

  1. Guava RateLimiter实现接口API限流

    一.简介 Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率.RateLimit二的原理类似与令牌桶,它主要由许可发出的速率来定义,如果没有额外的配置,许可证将按每秒许可证规定的 ...

  2. 服务接口API限流 Rate Limit 续

    一.前言 上一篇文章中粗浅的介绍使用Redis和基于令牌桶算法进行对服务接口API限流,本文介绍另一种算法---漏桶算法的应用.Nginx想必大家都有所了解是一个高性能的 HTTP 和反向代理服务器, ...

  3. 服务接口API限流 Rate Limit

    一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...

  4. java 服务接口API限流 Rate Limit

    一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...

  5. Google Guava -缓存cache简单使用

    package guavacache; import java.util.concurrent.ExecutionException; import java.util.concurrent.Time ...

  6. Guava的RateLimiter在单机限流中的正确用法

    错误使用 在实现限流时,网上的各种文章基本都会提到Guava的RateLimiter,用于实现单机的限流,并给出类似的代码: public void method() { RateLimiter ra ...

  7. 使用AOP和Semaphore对项目中具体的某一个接口进行限流

    整体思路: 一 具体接口,可以自定义一个注解,配置限流量,然后对需要限流的方法加上注解即可! 二 容器初始化的时候扫描所有所有controller,并找出需要限流的接口方法,获取对应的限流量 三 使用 ...

  8. 实例:接口并发限流RateLimiter

    需求:接口每秒最多只能相应1个请求 1.创建 全局类对象 import com.google.common.util.concurrent.RateLimiter; import org.spring ...

  9. Spring Cloud限流详解

    转自:https://blog.csdn.net/tracy38/article/details/78685707 在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud ...

随机推荐

  1. Ansible Playbook 简介

    我们去远程执行命令时要使用 command 模块,拷贝文件时要使用 copy 模块,如果我们要操作的东西很多,那就要执行很多条不同模块的命令Playbook 是一个 yaml 配置文件,我们可以把不同 ...

  2. C++ template —— trait与policy类(七)

    第15章 trait与policy类---------------------------------------------------------------------------------- ...

  3. 安装Alcatraz来管理Xcode插件的步骤和注意点

    简介: Alcatraz 是一个帮你管理 Xcode 插件.模版以及颜色配置的工具.它可以直接集成到 Xcode 的图形界面中,让你感觉就像在使用 Xcode 自带的功能一样 下载和管理插件步骤 一 ...

  4. iptables 介绍

    规则链 规则链的作用:对数据包进行过滤或处理 链的作用:容纳各种防火墙规则 链的分类依据:处理数据包的不同时机 默认包括5种规则链 INPUT:处理入站数据包 OUTPUT:处理出站数据包 FORWA ...

  5. hibernate annotation多对多中间表添加其他字段的第三种方法

    本示例主要以学生(T_Student)和课程(T_Course)之间的多对多关系,中间表Score(分数),学生表和课程表是多对多关系,另外为他们的关系添加额外的字段---分数: T_Student类 ...

  6. 【Spring Boot&&Spring Cloud系列】Spring Boot中使用NoSql数据库Redis

    github地址:https://github.com/AndyFlower/Spring-Boot-Learn/tree/master/spring-boot-nosql-redis 一.加入依赖到 ...

  7. Android Studio项目提交(或更新)到github的方法

    一 配置github登陆信息 二 上传工程到github 1. 2. 点击Share按钮 3. 点击ok按钮 状态栏提示: 4. 点击 No 按钮 5.AS右下脚弹出提示框 查看github网站,已经 ...

  8. Word插入htm文件导致文本域动态增加的一个问题

    受html标签影响,超链接也会被变成文本域(HYPERLINK).当遍历文本域进行替换之前如果预存了文本域的数量(Count/Length/etc.)将导致遗漏.

  9. SOS does not support the current target architecture解决方法

    客户提交一个dump文件,WinDbg加载时出现大量WARNING,加载对应版本的SOS后执行相应命令提示"SOS does not support the current target a ...

  10. 关于数据库DB负载均衡的初步研究(二)

    负载均衡: 是什么:有一组服务器由路由器联系在一起,各个节点相互协作,共同负载,均衡压力. 实现原理:应用程序与DB之间有个中央控制台服务器,根据负载均衡策略决定访问哪一台DB服务器. DB服务器:读 ...