国际惯例原理图

代码实现

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源码分析之令牌桶-限流算法的更多相关文章

  1. Eureka 源码分析之 Eureka Server

    文章首发于公众号<程序员果果> 地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw 简介 上一篇文章<Eureka 源码分析 ...

  2. coding++:高并发解决方案限流技术-使用RateLimiter实现令牌桶限流-Demo

    RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率. 通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位时 ...

  3. 高并发解决方案限流技术-----使用RateLimiter实现令牌桶限流

    1,RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率.通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位 ...

  4. ASP.NET Core中使用令牌桶限流

    在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...

  5. 【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)

    前言介绍 了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进 ...

  6. 微服务之SpringCloud实战(四):SpringCloud Eureka源码分析

    Eureka源码解析: 搭建Eureka服务的时候,我们会再SpringBoot启动类加上@EnableEurekaServer的注解,这个注解做了一些什么,我们一起来看. 点进@EnableEure ...

  7. Eureka 源码分析之 Eureka Client

    文章首发于微信公众号<程序员果果> 地址:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ 简介 Eureka是一种基于REST(Repr ...

  8. Eureka源码分析

    源码流程图 先上图,不太清晰,抱歉 一.Eureka Server源码分析 从@EnableEurekaServer注解为入口,它是一个标记注解,点进去看 注解内容如下 /** * 激活Eureka服 ...

  9. 【图解源码】Zookeeper3.7源码分析,包含服务启动流程源码、网络通信源码、RequestProcessor处理请求源码

    Zookeeper3.7源码剖析 能力目标 能基于Maven导入最新版Zookeeper源码 能说出Zookeeper单机启动流程 理解Zookeeper默认通信中4个线程的作用 掌握Zookeepe ...

随机推荐

  1. Apollo源码打包及部署

    1. 通过源码打包 到携程Apollo地址 https://github.com/ctripcorp/apollo 下载Apollo源码,可在源码中进行自定义配置日志路径及端口等,之后打包. 打包完成 ...

  2. K8S集群Master高可用实践

    K8S集群Master高可用实践    https://blog.51cto.com/ylw6006/2164981 本文将在前文基础上介绍k8s集群的高可用实践,一般来讲,k8s集群高可用主要包含以 ...

  3. centos和windows添加路由命令记录

    # 默认路由做香港出口route add default gw 192.168.10.33route add default gw 192.168.10.1 # 删除默认路由# route del d ...

  4. 逐层解析请求json参数字符串【我】

    import net.sf.json.JSONObject; 逐层解析请求json参数字符串: InputStream inStream =null; BufferedReader br =null; ...

  5. Spring cloud微服务安全实战-5-9实现基于session的SSO(Token有效期)

    token的有效期 会出现一种情况session有效期还没到.但是token过期了. 用户登陆着,但是token失效了 没法访问服务了. 刷新令牌要和clientId和ClientSecret一起用, ...

  6. ES6深入浅出-4 迭代器与生成器-1.字面量增强

    今天的内容 字面量literal 写出来就是它的值 例如字符串hello.这就是自变量. 一个空对象,也是自变量 写出来就是代表它写出来的那个意思就是自变量. 与其相反的就是构造出来的.例如下面的ne ...

  7. [转载]Java迭代器(iterator详解以及和for循环的区别)

    Java迭代器(iterator详解以及和for循环的区别) 觉得有用的话,欢迎一起讨论相互学习~[Follow] 转载自 https://blog.csdn.net/Jae_Wang/article ...

  8. 【mysql】reset Password

    https://www.cnblogs.com/josn1984/p/8550419.html https://blog.csdn.net/l1028386804/article/details/92 ...

  9. 【Leetcode_easy】937. Reorder Log Files

    problem 937. Reorder Log Files solution: class Solution { public: vector<string> reorderLogFil ...

  10. Mathtype安装与最简破解

    1.MathType资源 链接: https://pan.baidu.com/s/1UapJCcfU7Me_rIWdAe5nfw   提取码:   1y9i 2.破解 我之前的30天试用期过了,没来得 ...