天安云谷网站建设,案例学习网站建设方案,全国性质的网站开发公司,公司装修款账务处理一.项目背景 最近项目中需要进行接口保护#xff0c;防止高并发的情况把系统搞崩#xff0c;因此需要对一个查询接口进行限流#xff0c;主要的目的就是限制单位时间内请求此查询的次数#xff0c;例如1000次#xff0c;来保护接口。 参考了 开涛的博客聊聊高并发系统限流… 一.项目背景 最近项目中需要进行接口保护防止高并发的情况把系统搞崩因此需要对一个查询接口进行限流主要的目的就是限制单位时间内请求此查询的次数例如1000次来保护接口。 参考了 开涛的博客聊聊高并发系统限流特技 学习了其中利用Google Guava缓存实现限流的技巧在网上也查到了很多关于Google Guava缓存的博客学到了好多推荐一个博客文章http://ifeve.com/google-guava-cachesexplained/,关于Google Guava缓存的更多细节或者技术这篇文章讲的很详细 这里我们并不是用缓存来优化查询而是利用缓存存储一个计数器然后用这个计数器来实现限流。 二.效果实验 static LoadingCacheLong, AtomicLong count CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoaderLong, AtomicLong() {Overridepublic 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;i100;i){new Thread(){Overridepublic 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 LoadingCacheLong, AtomicLong count CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoaderLong, AtomicLong() {Overridepublic 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){//获取当前的时间戳作为keyLong 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;i100;i){new Thread(){Overridepublic void run() {try {System.out.println(Counter.getCounter());}catch (Exception e){e.printStackTrace();}}}.start();Thread.sleep(100);}}
}在上述这样的情况下一个线程如果遇到当前时间正在执行的线程超过limit值就会一直在while循环这样会浪费大量的资源我们在做限流的时候如果出现这种情况可以不进行while循环而是直接抛出异常或者返回来拒绝这次执行查询这样便可以节省资源。 转载于:https://www.cnblogs.com/sunp823/p/5601395.html