【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 ...
随机推荐
- vue项目用户登录状态管理,vuex+localStorage实现
安装vuex cnpm install vuex --save-dev
- Java 面向对象(十五)
Lambda表达式 1. 函数式编程思想概述 在数学中,函数就是有输入量.输出量的一套计算方案,也就是"拿什么东西做什么事情".相对而言,面向对象过分强调"必须通过对象的 ...
- MySQL 正则(Regular Expression) 邮箱(Email)
MySQL 正则表达式 | 菜鸟教程https://www.runoob.com/mysql/mysql-regexp.html (1条消息)常用正则表达式—邮箱(Email) - Samuel - ...
- 识别哈希算法类型hash-identifier
识别哈希算法类型hash-identifier hash-identifier是一款哈希算法识别工具.通过该工具,用户可以识别哈希值所使用的哈希算法.确定算法后,就可以采用对应的工具进行xx.执行该命 ...
- netty5心跳与业务消息分发实例
继续基于我们之前的例子(参见netty5自定义私有协议实例),这次我们加上连接校验和心跳机制: 只要校验通过,客户端发送心跳和业务消息是两个不同的事件发送的,彼此互不干扰.针对以上流程,我们需要增加4 ...
- evo:评测slam参数设置
zn@buaa:~$ evo_ape euroc m1/state_groundtruth_estimate0/data.csv pl-svo-master2/trajout.txt -angle_d ...
- ES6深入浅出-9 Promise-2.Promise的用法
回调是不需要return 就可以传递数据.缺点就是嵌套多了就成了回调地狱 回调的另外一个问题拿不准应该怎么去传这个参数.以为node.js为例.nodejs有个readFile去读取文件,读取成功就用 ...
- PL-SQL Developer 配置使用Oracle客户端连接
没有在本地安装Oracle 的用户必须下载一个instantclient(即使客户端), 路径指向这里,才能正常使用 下载 instantclient-basic-nt-11.2.0.3.0.zip ...
- 邪淫真正的可怕危害 (转自学佛网:http://www.xuefo.net/nr/article54/544414.html)
邪淫真正的可怕危害 邪淫的害处可能很快就显现,也可能是逐渐的表现出来.但往往后者的害处更大.因为当积累了多年的邪淫果报一旦显现,后悔就已经晚了. 我本人就是一个很好的例子.回想自己这40多年的日子,邪 ...
- Win10下载安装PostgreSQL 11.1
下载地址:https://get.enterprisedb.com/postgresql/postgresql-11.1-1-windows-x64.exe Installation Director ...