国际惯例原理图

代码实现

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. 第06组 团队Git现场编程实战

    一.组员职责分工 队员姓名 主要分工 朱庆章 测评福州最受欢迎的商圈(参考人气) 陈梦雪 测评福州最受欢迎的商圈(参考人气) 关文涛 分别测评福州人均消费50以下,50-100.100-200.200 ...

  2. html常用标签详解

    html常用标签详解 一.总结 一句话总结: 这些资料没必要自己总结,可以直接网上找,简单方便,再根据需求改一下 二.HTML常用标签详解 转自或参考:HTML常用标签详解https://blog.c ...

  3. 如何将eclipse项目导入到idea

    intellij idea中文资料网上比较少,对于eclipse的项目如何导入intellij idea也没有完整的说明,本人在这里整理下,方便更多人加入到intellij idea的阵容里. 直接上 ...

  4. Java IO系统--RandomAccessFile

    RandomAccessFile 实现了DataOutput接口和DataInput接口.父类是Object,不继承任何的InputStream和OutStram. public class Rand ...

  5. taocrypt

    taocrypt MySQL Bugs: #25189: mysqld: coding.cpp:243: void TaoCrypt::Base64Decoder::Decode(): Asserti ...

  6. Flutter -------- 网络请求之HttpClient

    今天来说说Flutter中的网络请求,HttpClient网络请求,包含get,post get var data; _get() async { Map newTitle; var response ...

  7. UniPush使用指南

    原贴:https://ask.dcloud.net.cn/article/35622 UniPush使用指南 分类:uni-app Push unipush 从HBuilderX 1.7.2起,uni ...

  8. linux普通用户添加root权限

    新增一个普通用户并进入该用户: [root@VM_0_7_centos ~]# groupadd mall [root@VM_0_7_centos ~]# useradd mall -m -d /ho ...

  9. MySQL数据库双机热备------主-主备份配置

    MySQL数据库双机热备------主-主备份配置 实验环境: 主1数据库 192.168.1.1 centos6.5 x86_64 +MySQL5.5.35 主2数据库192.168.1.2  Wi ...

  10. xml文档操作

    /** * */package com.gootrip.util; import java.io.ByteArrayOutputStream;import java.io.File;import ja ...