常用的限流算法学习

常用的限流算法有漏桶算法和令牌桶算法,guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或者取不到立即返回失败。

限流算法:令牌桶算法、漏斗桶算法、基于redis的滑动窗口计数法

 

令牌桶算法

我们用的是guava的RateLimiter,用在处理请求时候,从桶中申请令牌,申请到了就成功响应,申请不到时直接返回失败;

代码示例:

package common.guava;import com.google.common.util.concurrent.RateLimiter;
import org.junit.Test;public class RateLimitTest {@Testpublic void use1(){RateLimiter rateLimiter = RateLimiter.create(5.0);for (int i = 0 ; i < 20; i++){//尝试获取令牌if (rateLimiter.tryAcquire()){System.out.println("获取令牌成功");//模拟业务执行
//                try {
//                    Thread.sleep(500);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }}else {System.out.println("获取令牌失败");}}}@Testpublic void test2(){RateLimiter rateLimiter = RateLimiter.create(5);long start = System.currentTimeMillis()/1000;for (int i = 0 ; i < 10; i++){System.out.println("----start----");//阻塞式放行rateLimiter.acquire();System.out.println("放行");System.out.println("-----end-----");}long end = System.currentTimeMillis() / 1000;System.out.println(String.format("耗时:%d s", (end - start)));}}

漏斗桶算法

漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

参考:https://www.cnblogs.com/xuwc/p/9123078.html

 

package flowLimit;import com.google.common.util.concurrent.RateLimiter;
import org.junit.Test;import java.util.concurrent.TimeUnit;/***  @author: weijie*  @Date: 2020/9/23 19:15*  @url:https://blog.csdn.net/cailianren1/article/details/85283044*/
public class LeakyBucketLimitTest {/*** @param qps 平均qps 控制接口的响应速率,响应速率越快处理请求越多* @param countOfReq 桶的大小,接受请求的最大值* @return*/public RateLimiter createLeakyBucket(int qps, int countOfReq){return RateLimiter.create(qps,countOfReq, TimeUnit.MILLISECONDS);}@Testpublic void run(){RateLimiter leakyBucket = createLeakyBucket(100, 1000);long start = System.currentTimeMillis()/1000;int countRequest = 200;for (int i = 0; i < countRequest; i++){
//            System.out.println("请求过来");leakyBucket.acquire();
//            System.out.println("业务处理");}long spend = System.currentTimeMillis()/1000 - start;System.out.println("处理的请求数量:" + countRequest +"," +""+"耗时:" + spend + "s " +",qps:" + leakyBucket.getRate()+",实际qps:"+Math.ceil(countRequest/(spend)));}
}

窗口计数法

优点:和令牌桶相比,这种算法不需要去等待令牌生成的时间,在新的时间窗口,可以立即处理大量的请求。
缺点:在一个窗口临界点的前后时间,比如时间窗口是1分钟,在59秒和1分01秒同时突发大量请求,极端情况下可能会带来 2 倍的流量,系统可能承受不了这么大的突发性流量

java实现的固定窗口计数法:

package common.flowLimit;import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/***  @author: weijie*  @Date: 2020/9/23 18:02*  @Description:*  @url: https://blog.csdn.net/king0406/article/details/103129530?*/
public class WindowLimiter {Logger log = LoggerFactory.getLogger(WindowLimiter.class);//本地缓存,以时间戳为key,以原子类计数器为valueprivate LoadingCache<Long, AtomicLong> counter =CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build(new CacheLoader<Long, AtomicLong>() {@Overridepublic AtomicLong load(Long seconds) throws Exception {return new AtomicLong(0);}});private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);//设置限流阈值为15private long limit = 15;/*** 固定时间窗口* 每隔5s,计算时间窗口内的请求数量,判断是否超出限流阈值*/@Testpublic void run(){while (true){fixWindow();}}private void fixWindow() {scheduledExecutorService.scheduleWithFixedDelay(() -> {try {// time windows 5 slong time = System.currentTimeMillis() / 5000;//模拟每秒发送随机数量的请求int reqs = (int) (Math.random() * 5) + 1;long num = counter.get(time).addAndGet(reqs);log.info("time=" + time + ",num=" + num);if (num > limit) {log.info("限流了,num=" + num);}} catch (Exception e) {log.error("fixWindow error", e);} finally {}}, 0, 1000, TimeUnit.MILLISECONDS);}
}

基于redis分布式固定窗口计数法:

package flowLimit;import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;import java.util.Random;/***  @author: weijie*  @Date: 2020/9/23 18:11*  @url: https://blog.csdn.net/king0406/article/details/103130327*/
public class WindowLimiterByRedisTest {Logger logger = LoggerFactory.getLogger(WindowLimiterByRedisTest.class);JedisPool jedisPool;@Beforepublic void init(){String host = "39.96.204.209";int port = 6379;jedisPool = new JedisPool(host, port);}@Testpublic void run(){/*每次请求进来,查询一下当前的计数值,如果超出请求数阈值,则拒绝请求,返回系统繁忙提示*/Jedis redis = jedisPool.getResource();redis.auth("123456");long limit = 10;while (true){Random random = new Random();//模拟三个不同的请求String request = "flow:" + random.nextInt(3);long count = 0;try {count = limitFlow(redis, request);//超过限流if (count > limit){logger.error("当前访问过于频道,请稍后再试");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}else {logger.info("请求放行,执行业务处理");}}catch (Exception e){e.printStackTrace();}}}private long limitFlow(Jedis jedis, String key) {//Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。设置成功返回1,设置失败返回0Long lng = jedis.setnx(key, "1");if (lng == 1) {//设置时间窗口,redis-key时效为10秒jedis.expire(key, 10);return 1L;} else {//Redis Incrby 命令将 key 中储存的数字加上指定的增量值。相当于放在redis中的计数器,每次请求到来计数器自增1System.out.println("key: " + key);String va = jedis.get(key);System.out.println("value: " + va);long val = jedis.incr(key);System.out.println("result: " + val);return val;}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于rocketMq秒杀系统demo

基于RocketMQ设计秒杀。 要求&#xff1a; 1. 秒杀商品LagouPhone&#xff0c;数量100个。 2. 秒杀商品不能超卖。 3. 抢购链接隐藏 4. NginxRedisRocketMQTomcatMySQL 实现 接口说明&#xff1a;https://www.liuchengtu.com/swdt/#R9f978d0d00ef9be99f0…

基于Curator实现dubbo服务自动注册发现

文章目录概念基于ServiceDiscovery实现服务自动注册和发现Service:服务基本信息InstanceDetails:封装实例用过来保存到zk中ServiceProvider&#xff1a;服务提供者ServiceConsumer&#xff1a;服务消费者运行基于ServiceDiscovery、ServiceCache实现服务自动注册和发现Registry…

jdk、cglib动态代理代码示例

文章目录jdk动态代理实现步骤代码示例新建一个接口新建一个接口的实现类新建一个代理类调用测试cglib动态代理实现实现步骤创建一个实现类新建一个代理类调用测试jdk动态代理 实现步骤 新建一个接口新建一个接口的实现类新建一个代理类&#xff0c;实现InvocationHandler接口…

Netty 客户端服务器端通信 demo

服务端 package com.demo.rpc.netty;import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketC…

Solr实战篇

1.在MySQL数据中建立lagou_db数据库, 将position.sql中的数据导入到mysql 数据中。 2.使用Solr的DIH 将mysql中的数据导入到Solr中。 3.使用SpringBoot 访问Solr 使用positionName 字段检索职位信息 如果检索到的职位信息不够5条 则需要 启用positionAdvantage 查找 美女多、…

Docker 部署java服务

作业描述&#xff1a; &#xff08;1&#xff09;Hot是应用程序(springboot)&#xff0c;打成jar包&#xff1a;docker-demo-1.0-SNAPSHOT.jar &#xff08;2&#xff09;利用dockerfile将docker-demo-1.0-SNAPSHOT.jar构建成镜像docker-demo Dockerfile-docker-demo&#xf…

单向链表 双向链表 java代码实现

文章目录单向链表代码实现单元测试控制台打印头插法尾插法双向链表代码实现单元测试控制台打印头插法尾插法单向链表 代码实现 package csdn.dreamzuora.list;/*** author: weijie* Date: 2020/10/15 15:28* Description:*/ public class SingleNode {int id;String name…

栈、队列 java代码实现

文章目录普通队列数组实现java代码实现单元测试控制台打印链表实现java代码实现单元测试控制台打印LinkedList队列使用优先队列&#xff1a;PriorityQueue使用栈数组实现java代码实现单元测试控制台打印链表实现java代码实现单元测试控制台打印普通队列 概念&#xff1a;先入先…

ElasticSearch入门篇

文章目录控制台RESTFULL操作REST风格说明基于REST命令说明&#xff1a;es支持的数据类型核心数据类型ik分词器使用ik_smart最少切分ik_max_word最细粒度索引操作索引库创建创建文档方式指定索引类型(以后这种方式会被弃用)不指定索引类型利用默认的_doc类型更新文档方式一&…

【待完成】并发包下常用到线程工具类汇总

文章目录并发容器BlockingQueueArrayBlockingQueueLinkedBlockingQueuePriorityBlockingDelayQueueSynchronousQueueBlockingDequeCopyOnWriteCopyOnWriteArrayListCopyOnWriteArraySetConcurrentLinkedQueue/DequeConcurrentHashMapConcurrentSkipListMap/Set同步工具类AQS实现…

爬虫中如何获取页面编码类型

获取页面的编码格式的三种方式&#xff1a; 根据Response中的header获取编码格式根据页面标签中的meta获取根据页面内容识别自动识别出编码格式&#xff0c;经过测试准确率比较高 三种方式可以结合使用&#xff0c;由于inputStream不能够被复用&#xff0c;但是inputStrem没有…

Spark集群部署与架构

在大数据时代&#xff0c;处理海量数据需要分布式计算框架。Apache Spark作为一种强大的大数据处理工具&#xff0c;可以在集群中高效运行&#xff0c;处理数十TB甚至PB级别的数据。本文将介绍如何构建和管理Spark集群&#xff0c;以满足大规模数据处理的需求。 Spark集群架构…

java核心技术-多线程并发设计原理以及常见面试题

文章目录写在前面多线程回顾Thread和Runnable面试官&#xff1a;为什么我们在项目中推荐使用使用Runnable方法而不用继承Thread&#xff1f;面试官&#xff1a;Callable为什么可以带返回值&#xff0c;你知道底层原理&#xff1f;面试题&#xff1a;线程了解&#xff1f;给我讲…

java核心技术-jvm基础知识

文章目录JVM回顾JVM、JRE、JDK之间关系&#xff1f;Java程序执行过程&#xff1f;面试官&#xff1a;解释执行和JIT(及时编译)两种执行方式有什么区别&#xff1f;java虚拟机内存管理jvm整体架构JVM只是定义内存划分规范等&#xff0c;具体实现依赖不同虚拟机实现&#xff0c;如…

多线程场景下利用ThreadLocal是线程安全?

文章目录背景多线程场景测试代码结论背景 ThreadLocal原理以及基本概念这里我就不介绍了&#xff0c;这里我们主要关注ThreadLocal是否是线程安全吗&#xff1f;其实如果我们知道ThreadLocal原理我们肯定知道它是线程安全的&#xff0c;但是我在开发的时候不放心做了个测试&am…

深入剖析线程池基本原理以及常见面试题详解

文章目录面试官&#xff1a;能给我讲讲线程池的实现原理&#xff1f;线程池类继承关系ThreadPoolExecutor核心数据结构面试官&#xff1a;给我讲讲线程池的有哪些参数&#xff1f;面试官&#xff1a;如何优雅的关闭线程&#xff1f;线程的生命周期面试官&#xff1a;线程池哪五…

设计模式七大设计原则

文章目录设计模式七大设计原则开闭原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则-最少知道原则单一职责原则合成复用原则设计模式 面向对象的三个基本特征&#xff1a; 继承封装多态 设计模式体现了代码的耦合性、内聚性、可维护性、可扩展性、重用性、灵活性。 代码…

从框架源码中学习结构型设计模式

文章目录从框架源码学习结构型设计模式适配器模式应用实例案例一&#xff1a;dubbo框架日志适配器Logger接口日志实现类Logger适配器接口LoggerAdapter实现类Logger日志工厂桥接模式应用场景案例&#xff1a;dubbo源码-远程调用模块channelHandler设计ChannelHandler是一个SPI拓…

MDC日志logback整合使用

MDC日志logback整合使用 为什么使用MDC记录日志&#xff1f; 场景&#xff1a; 由于我的搜索服务并发量比较高&#xff0c;而处理一次搜索请求需要记录多个日志&#xff0c;因此日志特别多的情况下去查一次搜索整个日志打印情况会比较复杂。 解决方案&#xff1a; 可以使用用…

如何合理的配置线程数?

文章目录题记Java并发编程实战美团技术团队追求参数设置合理性线程池参数动态化题记 我想不管是在面试中、还是工作中&#xff0c;我们总会面临这种问题&#xff0c;那么到底有没有一种计算公式去告诉我们如何去配置呢&#xff1f; 答案是&#xff1a;没有 想要合理的配置线…