限流目的

限流的目的是防止恶意请求流量、恶意攻击、或者防止流量超过系统峰值。

流量达到峰值时,会有一个熔断策略,常见的熔断策略:

  • 直接拒绝请求,跳转到一个“服务器出小差”页面
  • 排队等待,比如火车票等待。
  • 服务降级,返回最基本的用户可接受的数值。
    • 到达峰值的时候进行排队。
    • 定位经纬度定位,精确定位到达峰值,返回大概定位,比如百度地图,有时候可以定位到自己的详细位置,有时候显示自己所在的城市。

限流实现的方式有很多种,比如nginx限流,网关限流,或者在代码中限流。具体如何限流,也要根据不同的场景进行选择。以下介绍下如何在代码中进行限流。

限流方式

漏桶

​ 漏桶是有一个进水口,一个出水口。桶本身具有一个恒定的速率往下漏水,而上方时快时慢的会有水进入桶内。 进水速度可以大于出水速度,但是多余的水会积在桶中,一旦水满,上方的水就无法加入。

特点:漏水速度固定。

令牌桶

令牌桶是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。当请求到达当时,从桶中拿令牌,有令牌即可进行请求。

限流实现

单机限流

1.Semaphore限流

​ Semaphore是jdk自带的一种并发限流方式。通过new Semaphore(2);可设置并发数为2。切记获取资源后,要进行资源释放。

public static void main(String[] args) {//并发为2个    Semaphore semaphore = new Semaphore(2);Random random = new Random();for(int i = 0 ; i<5;i++){new Thread(()->{try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+"获取权限");Thread.sleep(random.nextInt(2000)); // 仿造处理时间} catch (InterruptedException e) {e.printStackTrace();}finally {semaphore.release(); //注意一定要finally中进行释放资源System.out.println(Thread.currentThread().getName()+"释放");}}).start();}}
  • Semaphore.acquire/release方法要配对使用,使用acquire申请资源,使用release释放资源。Semaphore即使在没有申请到资源的情况下,还是可以通过release释放资源,这里就要自己通过代码进行合适的处理。和锁不同的是,锁必须获得锁才能释放锁,这里并没有这种限制。
  • Semaphore.release方法尽可能的放到finally中避免资源无法归还。
  • 如果Semaphore的配额为1,那么创建的实例相当与一个互斥锁,由于可以在没有申请资源的情况调用release释放资源,所以,这里允许一个线程释放另一个线程的锁。

2.RateLimiter限流

​ Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。更多信息。

​ 使用Guava的RateLimiter类通过令牌桶进行限流。

引入jar

       <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>28.1-jre</version></dependency>
1普通令牌桶
 public static void main(String[] args) {RateLimiter r = RateLimiter.create(5);for(;;){System.out.println("get 1 tokens: " + r.acquire() + "s");}/*** output: 基本上都是0.2s执行一次,符合一秒发放5个令牌的设定。* get 1 tokens: 0.0s* get 1 tokens: 0.182014s* get 1 tokens: 0.188464s* get 1 tokens: 0.198072s* get 1 tokens: 0.196048s* get 1 tokens: 0.197538s* get 1 tokens: 0.196049s*/}
  • ​ 通过RateLimiter.create(5);方法声明每秒创建5个令牌。

  • acquire方法进行获取令牌,并且返回获取时间。

令牌积累情况

 public static void main(String[] args) throws InterruptedException {RateLimiter r = RateLimiter.create(2);for(;;){System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");Thread.sleep(2000l);System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");}/*** get 1 tokens: 0.0s* get 1 tokens: 0.49874s*  休眠2 秒* get 1 tokens: 0.0s* get 1 tokens: 0.0s  // 积累了两个* get 1 tokens: 0.0s  // 预支1个,预支是什么?下面说* get 1 tokens: 0.499827s* get 1 tokens: 0.499159s* get 1 tokens: 0.49749s* get 1 tokens: 0.499572s*/}

在没有足够令牌发放时,采用滞后处理的方式,也就是前一个请求获取令牌所需等待的时间由下一次请求来承受,也就是代替前一个请求进行等待。

    public static void main(String[] args) throws InterruptedException {RateLimiter r = RateLimiter.create(2);System.out.println("get 1 tokens: " + r.acquire(4) + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");System.out.println("get 1 tokens: " + r.acquire() + "s");/**get 1 tokens: 0.0s        第一次获取4个get 1 tokens: 1.998205s  ,第二次获取要为第一次买单,所以等待两秒,如果第一次获取6个,则就是等待3秒。get 1 tokens: 0.496947sget 1 tokens: 0.499579s*/}
2.预热令牌桶

RateLimiter.create(2, 3, TimeUnit.SECONDS);说明需要3秒的预热时间,开始的时候是达不到每秒中2个令牌,在3秒内创建令牌速度逐渐加快,最后达到每秒钟2个令牌。
预热令牌桶场景:可能有缓存失效的时候,这段时间需要和数据库交互,进行一个缓存的预热。

public static void main(String[] args) {RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS);for (;;){System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("end");/*** output:* get 1 tokens: 0.0s* get 1 tokens: 1.329289s* get 1 tokens: 0.994375s* get 1 tokens: 0.662888s  上边三次获取的时间相加正好为3秒* end* get 1 tokens: 0.49764s  正常速率0.5秒一个令牌* get 1 tokens: 0.497828s* get 1 tokens: 0.49449s* get 1 tokens: 0.497522s*/}
}

参考资料:超详细的Guava RateLimiter限流原理解析

集群限流

redis限流

在正式的开发过程中,往往是集群化部署的,那么上面的单机限流就力不从心了。可以采用redis进行集群限流。redission框架通过lua脚本实现的集群限流。
redission通过getRateLimiter方法进行限流,使用方式和guava的限流类似。

  public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient client = Redisson.create(config);RRateLimiter rateLimiter = client.getRateLimiter("order"); RateType.OVERALL,    //所有客户端加总限流,就是集群下所有的流量 RateType.PER_CLIENT; //每个客户端单独计算流量,每台机器的流量rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {executorService.submit(() -> {try {long start = System.currentTimeMillis();rateLimiter.acquire();System.out.println("线程" + Thread.currentThread().getName() + "进入数据区:" + (start-System.currentTimeMillis()));} catch (Exception e) {e.printStackTrace();}});}/*** 线程pool-1-thread-4进入数据区:-10* 线程pool-1-thread-5进入数据区:-13* 线程pool-1-thread-7进入数据区:-1010* 线程pool-1-thread-10进入数据区:-1013* 线程pool-1-thread-1进入数据区:-2017* 线程pool-1-thread-9进入数据区:-2016* 线程pool-1-thread-8进入数据区:-3021* 线程pool-1-thread-3进入数据区:-3022* 线程pool-1-thread-2进入数据区:-4028* 线程pool-1-thread-6进入数据区:-4028*/}

Sentinel限流

Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel官方文档

【开发经验】java代码中实现限流相关推荐

  1. 高并发中的 限流、熔断、降级、预热、背压你都知道是什么意思吗?

    首先,我们需要明确一下这几个名词出现的场景:分布式高并发环境.如果你的产品卖相不好,没人鸟它,那它就用不着这几个属性.不需要任何加成,低并发系统就能工作的很好. 分布式系统是一个整体,调用关系错综复杂 ...

  2. 如何在java代码中读取配置文件

    在日常开发过程中,我们经常需要拼接一些字符串之类的东西,而这些字符串往往是不变的,或者在java代码中多次使用到的.当然我们可以在java代码中写死,但是这样做的缺点也是有目共睹的,一旦业务需求发生变 ...

  3. 你还在 Java 代码中写 set/get 方法?赶快试试这款插件吧!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:Mr.ml https://blog.csdn.net/Ma ...

  4. java代码中fastjson生成字符串和解析字符串的方法和javascript文件中字符串和json数组之间的转换方法...

    1.java代码中fastjson生成字符串和解析字符串的方法 List<TemplateFull> templateFulls = new ArrayList<TemplateFu ...

  5. idea自动生成get set_CTO:不要在Java代码中写set/get方法了,逮一次罚款

    前言 what?你的 Java 代码中还充斥着大量的 set/get 方法? 我们在刚开始学习 Java 语言的时候讲过,面向对象的三大特征就是封装,继承,和多态.在 Java 中,要保证封装性,需要 ...

  6. Android如何在java代码中设置margin

    Android如何在java代码中设置margin,也就是组件与组件之间的间距. 代码中设置: LinearLayout.LayoutParams params = new LinearLayout. ...

  7. 如何在android的XML和java代码中引用字符串常量

    使用字符串(string)资源        在一个Android工程中,我们可能会使用到大量的字符串作为提示信息.这些字符串都可以作为字符串资源声明在配置文件中,从而实现程序的可配置性. 在代码中我 ...

  8. java : enum、创建文件和文件夹、删除文件和文件夹、获得项目绝对路径、写入数据到excel中、java代码中两种路径符号写法、读取、写入text文件...

    java : enum http://www.cnblogs.com/hyl8218/p/5088287.html 创建文件和文件夹.删除文件和文件夹 http://www.cnblogs.com/m ...

  9. java中getup用法_你还在 Java 代码中写 set/get 方法?赶快试试这款插件吧!

    前言 what?你的 Java 代码中还充斥着大量的 set/get 方法? 我们在刚开始学习 Java 语言的时候讲过,面向对象的三大特征就是封装,继承,和多态.在 Java 中,要保证封装性,需要 ...

最新文章

  1. 格式工厂mac版_格式工厂无广告版,支持PDF文件的转换
  2. ML机器学习导论学习笔记
  3. vba fso读utf 文本_利用FSO对象操作文件
  4. Windows 2008活动目录的安装和卸载
  5. python怎么索引txt数据中第四行_python txt读取第一行数据库
  6. fir滤波器matlab实现_关于FIRamp;IIR系统的算法说明以及结果验证(1)
  7. python写web自动化_jenkins+selenium+python实现web自动化测试
  8. 老鸟对菜鸟的一些建议
  9. 遗传相似系数怎么计算_如何计算遗传变异系数
  10. hdfs读写流程_一篇文章搞清楚 HDFS 基本原理
  11. Shopee Games 游戏引擎演进之路
  12. Slickedit 打开Qt工程
  13. 我用 Python 自制成语接龙小游戏,刺激!
  14. 迅雷版权限制无法下载破解
  15. CF802C Heidi and Library (hard) (网络流+最大流)
  16. 《实用软件工程答案》张海涛人民邮电出版社
  17. CGAL例程:地理信息系统----点云数据生成DSM、DTM、等高线和数据分类
  18. 基于ESP32+AMG8833的物联网红外成像测温枪
  19. 技术分享:应用于厚型气体电子倍增器的高耐压PCB研究
  20. 韩信点兵 中国剩余定理

热门文章

  1. Android 实现拍照功能,并将图片保存到本地存储
  2. 解题:POI 2009 Lyz
  3. Java绘制太极阴阳图
  4. 在CentOS 7 安装Calamari
  5. Apple设备型号名称映射表
  6. 坚定信念,踏踏实实走好脚下的每一步!
  7. 人工智能实践:tensorflow笔记
  8. 存档:全球各国名称中英文对照表
  9. free, iostat, Linux, top, uptime, vmstat
  10. 7-4 房产税费计算2022.6.24