【springcloud】2.eureka源码分析之令牌桶-限流算法
国际惯例原理图

代码实现
package Thread; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; /**
* @ProjectName: cutter-point
* @Package: Thread
* @ClassName: RateLimiter
* @Author: xiaof
* @Description: 令牌桶,限流
* @Date: 2019/6/21 11:41
* @Version: 1.0
*/
public class RateLimiter { //限流消费的令牌
private final AtomicInteger consumedTokens = new AtomicInteger(); private final AtomicLong lastRefrushTokenTime = new AtomicLong(0); //限流类型,是秒,还是分
private final long rateType; public RateLimiter(TimeUnit averageRateUnit) {
switch (averageRateUnit) {
case SECONDS:
rateType = 1000;
break;
case MINUTES:
rateType = 60 * 1000;
break;
default:
throw new IllegalArgumentException("TimeUnit of " + averageRateUnit + " is not supported");
}
} //请求令牌,判断是否可以获取到新的令牌
public boolean acquire(int bucketSize, long avgRate) {
return acquire(bucketSize, avgRate, System.currentTimeMillis());
} public boolean acquire(int bucketSize, long avgRate, long curentTimeMillis) { if(bucketSize <= 0 || avgRate <= 0) {
return true;//如果这2个参数,任意一个为0 ,我们就认为没有上限
} //刷新令牌桶
refillToken(bucketSize, avgRate, curentTimeMillis);
//开始消费令牌
return consumToken(bucketSize);
} private void refillToken(int bucketSize, long avgRate, long currentTimeMillis) {
//获取上次最后以后更新令牌时间
long freshTime = lastRefrushTokenTime.get();
//获取当前间隔时间
long timeDelta = currentTimeMillis - freshTime; //计算这次需要填充的token数
long newToken = timeDelta * avgRate /rateType;
if(newToken > 0) {
//新的更新时间
long newFillTime = freshTime == 0 ? currentTimeMillis : freshTime + timeDelta;
//用cas操作,以保证只有一个线程注入新令牌
if(lastRefrushTokenTime.compareAndSet(freshTime, newFillTime)) {
//死循环,直到设置成功新的令牌
while(true) {
//1.获取当前消费的令牌
int currentConsumToken = consumedTokens.get();
//2.获取消费的令牌容量,跟桶极限大小比较,取小的那个
int realConsumTokens = Math.min(currentConsumToken, bucketSize);
//3.计算填充之后剩余的被消费的容量,计算新增容量,用来填充被消费的令牌数
//剩余的消费容量,但是不能比0还小,这个要取值
int newConsumSize = (int) Math.max(0, realConsumTokens - newToken);
//然后设置进去
if(consumedTokens.compareAndSet(currentConsumToken, newConsumSize)) {
return;
}
}
}
}
} //消费令牌
private boolean consumToken(int bucketSize) {
while (true) {
int currentLevel = consumedTokens.get();
//如果超出负载
if (currentLevel >= bucketSize) {
return false;
}
//每次消费一个
if (consumedTokens.compareAndSet(currentLevel, currentLevel + 1)) {
return true;
}
}
} public void reset() {
consumedTokens.set(0);
lastRefrushTokenTime.set(0);
} }
到这里可能有的人不清楚怎么用,来我们测试一波
我们假设有100个用户同时请求,然后令牌恢复速率调成10,然后速率单位改为秒,也就是1秒恢复10个令牌
这样同时100个请求过来,马上令牌就会被用完,那么就会被限流,比如我们拦截器这个时候可以返回404,或者503
public static void main(String args[]) {
RateLimiter rateLimiter = new RateLimiter(TimeUnit.SECONDS);
final int bucketSize = 10;
//回复令牌生产速率
final long avgRate = 10;
//判断是否流量达到上限
ExecutorService pool = Executors.newCachedThreadPool();
for(int i = 0; i < 100; ++i) {
pool.submit(new Runnable() {
@Override
public void run() {
while(true) {
try {
Thread.sleep(((int) (Math.random() * 10)) * 1000) ;
if(!rateLimiter.acquire(bucketSize, avgRate)) {
System.err.println(Thread.currentThread().getName() + "已经限流成功----response.setStatus(404)");
} else {
System.out.println(Thread.currentThread().getName() + "正常执行");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
while(true) {
}
}
效果展示

【springcloud】2.eureka源码分析之令牌桶-限流算法的更多相关文章
- Eureka 源码分析之 Eureka Server
文章首发于公众号<程序员果果> 地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw 简介 上一篇文章<Eureka 源码分析 ...
- coding++:高并发解决方案限流技术-使用RateLimiter实现令牌桶限流-Demo
RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率. 通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位时 ...
- 高并发解决方案限流技术-----使用RateLimiter实现令牌桶限流
1,RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率.通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位 ...
- ASP.NET Core中使用令牌桶限流
在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...
- 【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)
前言介绍 了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进 ...
- 微服务之SpringCloud实战(四):SpringCloud Eureka源码分析
Eureka源码解析: 搭建Eureka服务的时候,我们会再SpringBoot启动类加上@EnableEurekaServer的注解,这个注解做了一些什么,我们一起来看. 点进@EnableEure ...
- Eureka 源码分析之 Eureka Client
文章首发于微信公众号<程序员果果> 地址:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ 简介 Eureka是一种基于REST(Repr ...
- Eureka源码分析
源码流程图 先上图,不太清晰,抱歉 一.Eureka Server源码分析 从@EnableEurekaServer注解为入口,它是一个标记注解,点进去看 注解内容如下 /** * 激活Eureka服 ...
- 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码
Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...
随机推荐
- 第08组 Alpha冲刺(1/6)
队名:955 组长博客:https://www.cnblogs.com/cclong/p/11841141.html 作业博客:https://edu.cnblogs.com/campus/fzu/S ...
- 【软工实践】团队项目Snug-选题报告
组长博客链接 组长博客 NABCD分析引用 NEED 需求 根据我们的调查显示,大部分人都有着不规律的生活习惯,他们都希望有一款软件能够帮助他们,养成一个适合自己的较规律的生活习惯.我们的Snug正是 ...
- centos 普通用户 和 root 相互切换方法
root 用户切换为普通用户 :用 login -f username (加 -f 不用输入密码)例如普通用户的用户名为hadoop,这里就是 login -f hadoop 普通用户切换为root用 ...
- NPVariant -js传递给NPAPI插件参数在firefox和chrome需要采用不同的获取方式
原帖地址:http://blog.sina.com.cn/s/blog_4c6631790102wd1o.html 整数参数 typedef struct _NPVariant { NPVariant ...
- Makefile教程(绝对经典,所有问题看这一篇足够了)【转】
该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客: http://blog.csdn.net/haoel/article/details/2886 makefile很重 ...
- Git: git tag 使用小结(给发布版本打标记,切换并修改某个历史版本)
通常在软件发布的时候会打一个tag,用于标注这次发布的相关信息, 这样做的好处是,将来如果这个版本出现了问题,可以通过tag迅速定位到当前版本,进行错误修复. 1. 新建tag $ git tag v ...
- ISO/IEC 9899:2011 条款6.5——表达式
6.5 表达式 1.一个表达式是操作符与操作数的一个序列,这些操作符与操作数指定了一个值的计算,或指派一个对象或一个函数,或是生成副作用,或执行上述操作的组合.对一个操作符的操作数的值计算顺序排在对该 ...
- Spring cloud微服务安全实战-5-4请求转发及退出
步骤1234已经完成 下面处理 5678这几步.zuul在转发的时候 把请求头加上 获取订单信息 加一个按钮,登陆成功后,去拿订单的信息. 展示获取到的订单信息 ts代码 ts内定义order对象 定 ...
- Qt Http get
1.直接建立连接,向网站发送http请求 QNetworkAccessManager *accessManager = new QNetworkAccessManager(this); connect ...
- 【Mac】解决macos安装升级时报错安装所选更新时发生错误的问题
1 系统更新macjave 更新一直失败: 2 解决方法为:重新启动Mac,并按住Command+R进入恢复模式,找到Terminal后输入csrutil disable,然后重启Mac,再次下载并 ...