Java RateLimiter的限流详解
作者:温娉哲 发布时间:2023-01-06 17:14:12
限流背景
在早期的计算机领域,限流技术(time limiting)被用做控制网络接口收发通信数据的速率。可以用来优化性能,减少延迟和提高带宽等。现在在互联网领域,也借鉴了这个概念,用来为服务控制请求的速率,如双十一的限流,12306的抢票等。即使在细粒度的软件架构中,也有类似的概念。
系统在使用下游资源时,需要考虑下游对资源受限,处理能力,在下游资源无法或者短时间内无法提升处理性能的情况下,可以使用限流器或者类似保护机制,避免下游服务崩溃造成整体服务的不可用。
限流相关概念
在介绍限流之前先介绍几个容易混淆的概念,包括服务熔断,服务降级,服务隔离。
服务熔断
理解熔断之前先了解另一个概念:微服务的雪崩效应。因为熔断机制通常是作为应对雪崩效应的一种微服务链路保护机制。
在微服务架构中,一个微服务通常是完成一个单一业务功能的独立应用。这样做的好处是各个业务功能之间最大可能的解耦,每个微服务可以独立演进。通常一个应用可能会有很多个微服务组成,服务间通过RPC互相调用。假如有如下服务调用链路:
A,B依赖C去调用E,F。如果E服务不能正常提供服务了,C的超时重试机制将会执行。同时新的调用不断产生,会导致C对E服务的调用大量的积压,产生大量的调用等待和重试调用,慢慢会耗尽C的资源,比如内存或CPU,同时影响C调F,最终整个应用不可用。本例中由于链路上E的故障,对微服务A,B的调用就会占用越来越多的系统资源,进而引起系统崩溃,即所谓的“雪崩效应”。
熔断机制是应对雪崩效应的一种微服务链路保护机制,生活中有很多熔断的例子。比如电路中某个地方的电压过高,熔断器就会熔断,对电路进行过载保护。股市里边,如果股票指数涨跌幅过高,触及设置的熔断点后,随后的一段时间内将暂停交易。在微服务架构中的熔断机制作用类似。当调用链路的某个微服务不可用,或者响应时间太长,或者错误次数达到某个阈值,会进行服务熔断,即快速返回响应信息。当检测到该节点微服务调用响应正常后,逐步恢复正常的调用链路。
服务降级
服务降级主要是指在服务器压力陡增的情况下,根据某种策略对一些非核心服务或者页面不做请求处理或者简单处理,或者限流某个服务,从而释放服务器资源以保证核心业务正常运作或高效运作。比如京东618活动时,把无关交易的服务降级,比如关闭某个服务,或者查看历史订单,商品历史评论等非交易核心业务,只显示最近100条等。
服务隔离
隔离是指服务或者资源隔离开。服务隔离能够在服务发生故障时限定其影响范围,保证其他服务还是可用的。资源隔离一般是指通过隔离来减少服务间资源竞争。资源隔离的粒度有很多种,比如线程隔离,进程隔离,机房隔离等。线程隔离即隔离线程池资源,不同服务的执行使用不同的线程池。这样做的好处是即使其中一个服务线程池满了,也不会影响到其他的服务。比如下图中Tomcat处理请求,对每个微服务,都分配一个线程池。
服务限流
服务限流是限制请求的数量,即某个时间窗口内的请求速率。一旦达到限制速率则可以拒绝服务(定向到错误页或告知系统忙),排队等待(比如秒杀,用户评论,下单),降级(返回兜底数据或默认数据)。
比较
服务熔断,服务降级都是从系统的可用性角度考虑,防止系统响应延迟甚至崩溃而采用的技术性的系统保护手段。服务熔断一般是由某个下游服务故障引起,而服务降级一般是从整体业务的负载情况考虑,目的是为了降低系统负载。限流则是对单位时间内请求次数的限制。三者都是通过某种手段保证流量过载时系统的可用性。服务隔离则是让不同的业务使用各自独立的线程池资源,避免服务之间资源竞争的影响。
常见的限流方法
常见的限流手段有如下这些。限制总的并发数(比如数据库连接池,线程池),限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数),限制某个时间窗口内的平均速率(RateLimiter,nginx的limit_req模块);此外还有限制RPC调用频率,限制MQ的消费速率等。
限流工具类RateLimiter
google开源工具包guava提供了限流工具类RateLimiter,该类基于“令牌桶算法”,非常方便使用。
RateLimiter 使用Demo:
import com.google.common.util.concurrent.RateLimiter;
public class RateLimiterDemo {
public static void main(String[] args) {
// testNoRateLimiter();
testWithRateLimiter();
}
public static void testNoRateLimiter() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
System.out.println("call execute.." + i);
}
Long end = System.currentTimeMillis();
System.out.println(end - start);
}
public static void testWithRateLimiter() {
Long start = System.currentTimeMillis();
RateLimiter limiter = RateLimiter.create(10.0); // 每秒不超过10个任务被提交
for (int i = 0; i < 20; i++) {
limiter.acquire(); // 请求RateLimiter, 超过permits会被阻塞
System.out.println("call execute.." + i);
}
Long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
Guava版本
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
来源:https://blog.csdn.net/Sophia_0331/article/details/123782757


猜你喜欢
- 创建WebService项目首先安装下.NET Framework4.6.2-4.7.1开发工具。然后就是新建 ASP.NET Web应用程
- 这篇文章主要介绍了Java List集合排序实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 题目:将一个数组逆序输出。代码:import java.util.*;public class lianxi31 {public stati
- JPA的加锁机制有两种,乐观锁和悲观锁。乐观锁:乐观锁的特点在于认为数据冲突或者更新丢失等情况是很少发生的.当发生的时候,抛出异常和回滚就足
- 1、maven引入quartz包<!-- https://mvnrepository.com/artifact/org.quartz-
- 本文实例为大家分享了Android Camera1实现预览框显示的具体代码,供大家参考,具体内容如下Android要预览Camer界面其实非
- 前言想在锁屏上面实现弹窗,第一个想法就是利用 WindowManager 设置 Window 的 Flag,通过设置 Flag 的显示优先级
- 对象嵌套关联查询一对多List集合查询mybatis嵌套关联查询如下由于我的是一对集合查询,所以我有两个类。@Data@TableName(
- 1. 你可以讲下进程与线程的区别?为什么要用多线程?进程:进程是程序的一次执行过程,是系统运行程序的基本单位。线程:单个进程中执行中每个任务
- 1、导包基于maven<dependency> <groupId>com.fasterxml.jacks
- 具体代码如下所示:public class Student { private String id; private
- 不少朋友自己下载了一个Android SDK,怎样在Android studio中默认的Android SDK路径呢?打开Android s
- 简介本文主要介绍如何使用java代码利用Selenium操作浏览器,某些网页元素加载慢,如何操作元素就会把找不到元素的异常,此时需要设置元素
- 本文实例为大家分享了Java实现多线程在线聊天的具体代码,供大家参考,具体内容如下上一篇博客通过UDP实现了聊天,但只能单方面发送消息,这次
- 效果图如下:默认第一次加载选择原始队列:级联效果图:关键代码给下拉列表选中事件监听绑定Id :int pos = firsthand_dlb
- 昨天看了一段android配置aspectj实现AOP的直播视频,就试着自己配置了一下,可能是因为我自己的AndroidStudio环境的问
- 这篇文章主要介绍了SpringBoot实现 * 、过滤器、 * 过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 用java的框架和面板的知识做的一个展示月食过程的小程序。这里的想法就是先把背景设置成黑色,然后画一个黄色的圆作为月亮,接着画一个黑色的圆,
- 时间轴,顾名思义就是将发生的事件按照时间顺序罗列起来,给用户带来一种更加直观的体验。京东和淘宝的物流顺序就是一个时间轴,想必大家都不陌生,如
- 方案一.使用国内的镜像阿里仓库等首先通过maven的路径找到setting.xml的文件然后在其中修改mirror和profile保存一下就