个人网站:www.wangdatao.com

前言


分布式系统中进场会提到限速和降级的概念。所谓限流,可以认为是服务降级的一种,限流就是限制系统的输入和输出流量, 以达到保护系统的目的 系统上线之前,一般都会进行压测,压测之后吞吐量是可以被测算的,为了保证系统的稳定运行, 一旦达到了设定限制的阔值,就需要限制流量并采取一些措施以完成限制流目的。常见的限流方案为:延迟处理、拒绝处理和部分拒绝处理等。
一般高并发系统常见的限流有:限制总并发数(比如:数据库连接池、线程池)、限制瞬时并发数、限制时间窗口内的平均速率;其他还有如限制远程接口调用速率、限制MQ的消费速率;另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。


1、常用算法

1.1、计数器算法

算法思想:规定一个时间周期,统计在一个周期内总共请求的次数,如果时间超过一个周期,则次数计入到下一个周期内。
比如:当接收到第一个请求时,记录第一次的时间 beginTime, 计数器Counter设置为1,当第二个请求来时,判断第二个请求的时间与beginTime的间隔有没有超过最初设计的时间周期,如果没有超过,判断Counter是否超过设置的最大速度数,都满足时Counter加1;如果超过设计的时间周期,beginTime时间改为第二次请求的时间,Counter重置为1。


//不严谨的算法代码
public class CounterTest {public static long timeStamp = getNowTime();public static int reqCount = 0;public static final int limit = 3; // 时间周期内最大请求数public static final long interval = 1000; // 时间窗口mspublic static boolean grant() {long now = getNowTime();if (now < timeStamp + interval) {// 在时间窗口内reqCount++;// 判断当前时间窗口内是否超过最大请求控制数return reqCount <= limit;} else {timeStamp = now;// 超时后重置reqCount = 1;return true;}}public static long getNowTime() {return System.currentTimeMillis();}
}//很不严谨的调用测试
@RestController
@RequestMapping("/count")
public class CounterTestController {@GetMapping("/test1")public boolean test() {return CounterTest.grant();}
}

问题分析:当服务器最大的请求压力为100次每分钟时,有用户第一次请求的时间为第一秒,在第5960秒时请求了99次。由于次数有重置机制,在60秒后的第一次请求,统计会重置为1,那么算法上6061秒也是允许的。此时在59~61秒实际放入了199个请求。

思考:如果每分钟最大允许100个请求,把时间周期设置为30秒,30S内只允许50个请求,当出现上述临界问题时一分钟也不会超过100,这样做会带来什么问题吗?

1.2、滑动窗口算法(rolling window)

为了解决上述问题,我们引入了滑动窗口算法。把一个限速周期,分割成若干个时间小周期,每个小周期单独计数;当一个请求过来时,对应时间小周期的统计Counter加1。
到底怎么解决临界问题的?

时间周期的统计不以大周期统计,以小周期累加统计。比如每分钟限速100,把1分钟分为6个小周期,那么第二个大周期的计算为第10S-70S(1m10s),第三个周期计算标准为20S-80S 以此类推。
如果用户在第一个小周期请求一次触发计时,第六个周期(50~60)请求了99次,那么在第二个大周期统计的时间范围是10-70S,由于50-60S时已经请求了99次,那么在60-70S只允许请求1次,从而达到了限速效果。

import java.util.concurrent.atomic.AtomicInteger;//不严谨的代码实现
public class SlidingWindow {private AtomicInteger[] timeSlices;// 队列的总长度private final int timeSliceSize;// 每个时间片的时长 private final long timeMillisPerSlice;// 窗口长度 private final int windowSize;// 最大允许访问次数 private int maxVisitor;// 当前所使用的时间片位置 private AtomicInteger cursor = new AtomicInteger(0);// 默认窗口数量private final static int DEFAULT_WINDOW_SIZE = 5;// 默认时间片时长private final static Time DEFAULT_TIME = Time.SECONDS;// 默认时间片时长private final static int DEFAULT_MAX_VISITOR = 10;public static enum Time {MILLISECONDS(1),SECONDS(1000),MINUTES(SECONDS.getMillis() * 60),HOURS(MINUTES.getMillis() * 60),DAYS(HOURS.getMillis() * 24),WEEKS(DAYS.getMillis() * 7);private long millis;Time(long millis) {this.millis = millis;}public long getMillis() {return millis;}}public SlidingWindow() {this(DEFAULT_WINDOW_SIZE, DEFAULT_TIME, DEFAULT_MAX_VISITOR);}public SlidingWindow(int windowSize, Time timeSlice, int maxVisitor) {this.timeMillisPerSlice = timeSlice.millis;this.windowSize = windowSize;// 保证存储在至少两个windowthis.timeSliceSize = windowSize * 2 + 1;this.maxVisitor = maxVisitor;init();}/*** 初始化*/private void init() {AtomicInteger[] localTimeSlices = new AtomicInteger[timeSliceSize];for (int i = 0; i < timeSliceSize; i++) {localTimeSlices[i] = new AtomicInteger(0);}timeSlices = localTimeSlices;}private int locationIndex() {long time = System.currentTimeMillis();return (int) ((time / timeMillisPerSlice) % timeSliceSize);}/*** 判断是否允许进行访问,未超过阈值的话才会对某个时间片+1** @return*/public boolean allow() {int index = locationIndex();int sum = 0;int oldCursor = cursor.getAndSet(index);if (oldCursor != index) {timeSlices[index].set(0);clearBetween(oldCursor, index);}for (int i = 0; i < windowSize; i++) {sum += timeSlices[(index - i + timeSliceSize) % timeSliceSize].get();}// 阈值判断if (sum < maxVisitor) {// 未超过阈值才+1timeSlices[index].incrementAndGet();return true;}return false;}/*** <p>将fromIndex~toIndex之间的时间片计数都清零* <p>极端情况下,当循环队列已经走了超过1个timeSliceSize以上,这里的清零并不能如期望的进行** @param fromIndex 不包含* @param toIndex   不包含*/private void clearBetween(int fromIndex, int toIndex) {for (int index = (fromIndex + 1) % timeSliceSize; index != toIndex; index = (index + 1) % timeSliceSize) {timeSlices[index].set(0);}}public static void main(String[] args) {SlidingWindow window = new SlidingWindow(2, Time.MINUTES,5);for (int i = 0; i < 10; i++) {System.out.println(window.allow());}}
}

思考:优点-能解决临界问题。缺点-持续的高流量请求时,处理很容易集中在某一个时间格内。

1.3、漏桶算法

算法思想:有一个空的漏桶,上面不断的往里面倒水、下面不断的往外面放水;当倒水速度小于放水速度时,桶永远不会满;当倒水速度大于放水速度时,随着时间,桶迟早会满。


漏桶算法能把来不及处理的请求暂存在“桶”中,接口实现层能够按照“节奏”处理请求。需要注意的是桶的容量如果很大,水要流很久才能流完,可能会出现超时。

package com.datao.text.util;import java.util.concurrent.atomic.AtomicInteger;//不严谨的代码实现
public class LeakyBucket {//桶的容量private int capacity = 10;// 木桶剩余的水滴的量(初始化的时候的空的桶)private AtomicInteger water = new AtomicInteger(0);// 水滴的流出的速率 每1000毫秒流出1滴 private int leakRate;// 第一次请求之后,木桶在这个时间点开始漏水 private long leakTimeStamp;public LeakyBucket(int leakRate) {this.leakRate = leakRate;}public boolean acquire() {// 如果是空桶,就当前时间作为桶开是漏出的时间if (water.get() == 0) {leakTimeStamp = System.currentTimeMillis();water.addAndGet(1);return true;}// 计算时间间隔float intervalTime = ((float) (System.currentTimeMillis() - leakTimeStamp)) / 1000;// 计算时间间隔内流出了多少水int intervalTimeWater = Math.round(intervalTime * leakRate);// 计算剩余的水int waterLeft = water.get() - intervalTimeWater;// 剩余的水不能为负water.set(Math.max(0, waterLeft));// 尝试加水,并且水还未满if ((water.get()) < capacity) {water.addAndGet(1);// 重新更新leakTimeStamp leakTimeStamp = System.currentTimeMillis();return true;} else {// 水满,拒绝加水return false;}}public static void main(String[] args) throws InterruptedException {LeakyBucket leakyBucket = new LeakyBucket(3);for (int i = 0; i < 50; i++) {Thread.sleep(100);System.out.println(leakyBucket.acquire());}}
}

思考:为什么成功调用后更新时间,没成功调用不更新时间? 代码设计与漏洞思想有什么偏差,该怎么解决?

1.4、令牌桶算法

算法思想:有一个生产令牌车间,按照固定速率生产令牌,生成好放入到桶里;当桶满时,生产的令牌丢弃。每来一个请求,都会从桶里领一个令牌,如果拿不到令牌则等待或拒绝请求。


令牌桶算法能应对突发的桶内令牌个数的请求,当消耗完后,后续的处理速度就登录令牌产生的速度。与漏桶算法的区别是面大量突发请求时,令牌桶算法能及时处理对应令牌缓存的请求,漏桶算法只能等待慢慢“流出”。

2 快速实现

2.1 Google-Guava

2.1.1 引入Guava

  • Guava GitHub 地址:https://github.com/google/guava
  • 添加Guava Jar包
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>29.0-jre</version>
</dependency>

2.1.2 使用RateLimiter

  • 不严谨的使用方式
  • AccessLimitService.java 限流服务封装到一个类中AccessLimitService,提供tryAcquire()方法,用来尝试获取令牌,返回true表示获取到
@Service
public class AccessLimitService {//每秒只发出5个令牌RateLimiter rateLimiter = RateLimiter.create(5.0);/*** 尝试获取令牌* @return*/public boolean tryAcquire(){return rateLimiter.tryAcquire();}
}
  • Controller层每次收到请求的时候都尝试去获取令牌,获取成功和失败打印不同的信息
@Controller
public class HelloController {private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Autowiredprivate AccessLimitService accessLimitService;@RequestMapping("/access")@ResponseBodypublic String access(){//尝试获取令牌if(accessLimitService.tryAcquire()){//模拟业务执行500毫秒try {Thread.sleep(500);// service.do();}catch (InterruptedException e){e.printStackTrace();}return "aceess success [" + sdf.format(new Date()) + "]";}else{return "aceess limit [" + sdf.format(new Date()) + "]";}}
}

2.1.3 源码解析

  • 源码地址:GitHub
  • 源码讲解参考:https://www.jianshu.com/p/5d4fe4b2a726
  • 实现思想:令牌桶算法

2.2 Spring Boot 实现限速功能

由于Spring 没有封装好的工具可用,咱们可以使用上述的工具或算法实现

2.2.1 实现思路

①:自定义一个注解。
②:通过AOP切此注解,在切面里通过Guava实现限速功能。
③给需要限速的方法添加该注解。

2.2.1 实现的示例

  • 引入Guava
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>29.0-jre</version>
</dependency>
  • 自定义注解
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public  @interface ServiceLimit { String description()  default "";
}
  • AOP 实现类
@Component
@Scope
@Aspect
public class LimitAspect {每秒只发出5个令牌private static   RateLimiter rateLimiter = RateLimiter.create(5.0);//Service层切点  限流@Pointcut("@annotation(com.datao.config.aop.ServiceLimit)")  public void ServiceAspect() {}@Around("ServiceAspect()")public  Object around(ProceedingJoinPoint joinPoint) { Boolean flag = rateLimiter.tryAcquire();Object obj = null;try {if(flag){obj = joinPoint.proceed();}} catch (Throwable e) {e.printStackTrace();} return obj;}
}
  • 使用
    @Override@ServiceLimitpublic Result test() {//todo 操作}

改进思路:
实现不同方法不同的限速功能:

  • 在方法中加入枚举属性,不同的方法加入不同的枚举
  • 使用时在注解参数中传入
  • 在AOP中根据不同的属性实现不同的策略

2.3 SpringCloud Gateway 实现限速功能

2.3.1 使用上文算法

实现思路:

  • SpringCloud Gateway 过滤器
  • 过滤器介绍及使用:https://blog.csdn.net/forezp/article/details/85057268
  • 在过滤器中使用本文中上述算法或工具,根据自己的业务比如:IP限流、用户ID、接口等自定义实现限速限流。
  • 当然除了比较流行Guava外,还有其他工具:https://github.com/bbeck/token-bucket 或自行找一些更优质的工具使用。

2.3.2 使用Gateway自带配置

Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,使用Redis和lua脚本实现了令牌桶的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中:

以IP、URL、自定义参数限流示例:

  • 引入依赖
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>
  • 配置值文件中新增配置
spring:application:name: gatewaycloud:gateway:routes:- id: limit_routeuri: lb://dt-textpredicates:- Path= /text/**filters:- name: RequestRateLimiterargs:key-resolver: '#{@taoKeyResolver}'redis-rate-limiter.replenishRate: 1redis-rate-limiter.burstCapacity: 3redis:host: localhostport: 6379database: 0

配置关键词介绍:

key-resolver: 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
redis-rate-limiter.replenishRate: 令牌桶每秒填充平均速率。
redis-rate-limiter.burstCapacity: 令牌桶总容量。

  • 实现 TaoKeyResolver 类
package com.datao.gateway.config;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Objects;public class TaoKeyResolver implements KeyResolver {/*** 根据IP限流* @param exchange* @return*/@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());}//    /**
//     * 根据Url限流
//     * @param exchange
//     * @return
//     */
//    @Override
//    public Mono<String> resolve(ServerWebExchange exchange) {//        return Mono.just(exchange.getRequest().getURI().getPath());
//    }
//
//    /**
//     * 根据自定义参数限流
//     * @return
//     */
//    @Bean
//    public Mono<String> resolve(ServerWebExchange exchange) {//        return Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("tao")));
//    }@Beanpublic TaoKeyResolver taoKeyResolver() {return new TaoKeyResolver();}}
  • 由于设置的阈值比较小,手动刷新即刻触发限速

    • HTTP ERROR 429

2.3.3 lua源码解析

  • 上面测试后Redis中的缓存值:
  • Lua 使用教程:https://www.runoob.com/lua/lua-tutorial.html
  • SpringCloud Gateway中 lua 实现源码如下:
local tokens_key = KEYS[1]    #请求唯一标识
local timestamp_key = KEYS[2] #请求时间local rate = tonumber(ARGV[1])     #速率,如上面例子里的 1
local capacity = tonumber(ARGV[2]) #容量,如上面例子里的 3
local now = tonumber(ARGV[3])      #当前时间
local requested = tonumber(ARGV[4])#请求数量,默认是1local fill_time = capacity/rate
local ttl = math.floor(fill_time*2) #得到过期时间local last_tokens = tonumber(redis.call("get", tokens_key)) #剩余可用令牌,没有值则为桶的容量,上面例子里值范围是 0~3
if last_tokens == nil thenlast_tokens = capacity
endlocal last_refreshed = tonumber(redis.call("get", timestamp_key)) #上次请求时间,没值则为0
if last_refreshed == nil thenlast_refreshed = 0
endlocal delta = math.max(0, now-last_refreshed)     #单前时间与上次请求时间的差值,最小是0;
local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) #可用的令牌,最大为桶容量,范围是0~桶容量, 上面例子里是 0~3
local allowed = filled_tokens >= requested #是否允许请求, 可用令牌是否足够
local new_tokens = filled_tokens
local allowed_num = 0
if allowed thennew_tokens = filled_tokens - requested #可用令牌减去1allowed_num = 1
endredis.call("setex", tokens_key, ttl, new_tokens) #缓存可用令牌
redis.call("setex", timestamp_key, ttl, now)     #缓存当前时间return { allowed_num, new_tokens }

2.4 SpringCloud Zuul 实现限速功能

2.4.1 过滤器+上述算法

实现思路:在过滤其中+算法,使用Guava,或自定义算法加Redis,实现分布式限速。
具体实现:可参考这篇文章 https://blog.csdn.net/justlpf/article/details/84528669

2.4.2 基于配置

思路:Zuul 自带的没有限流的功能,需要引入第三方Jar包实现限速功能
工具:Spring-cloud-zuul-ratelimit https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

  • 引入依赖

    • 限速算法key等储存的不同位置,可能还需要引入不同的依赖,本文介绍储存在redis中。所以还需要引入redis的依赖。
<dependency><groupId>com.marcosbarbero.cloud</groupId><artifactId>spring-cloud-zuul-ratelimit</artifactId><version>2.0.6.RELEASE</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 新增配置
zuul:routes:dt_text:service-id: dt-textpath: /text/**stripPrefix: falseratelimit:key-prefix: rateenabled: truerepository: REDISdefault-policy-list:#    ########### 如下的配置就是说:每1秒内不能超过2个请求,2个请求时间加起来不能超过1秒(quota)############- limit: 2 #在一个单位时间窗口的请求数量quota: 1 1 #在一个单位时间窗口的请求时间限制(秒)refresh-interval: 60 #时间窗口 (in seconds)type: ##根据什么控制流量,可以组合使用,如url、httpmethod组合,就会把 /orders的get和post请求分开处理- url- http_method#- user  #根据用户控制需要Security支持,(一般不用)#- origin #根据客户端的ip控制

由于配置阈值比较低,手动在浏览器即可触发:

2.5 SpringCloud Alibaba Sentinel 实现限速功能

基本介绍:
官网地址:https://github.com/alibaba/Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:

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

2.5.1 快速开始

没有比官方文档更清晰的文档了,不介绍了。
https://github.com/alibaba/Sentinel/wiki/新手指南

2.6 Nginx 实现限速功能

Nginx主要有两种限速方式:

  • 按连接数限速: ngx_http_limit_conn_module
  • 按请求速率限速: ngx_http_limit_req_module

请各位大侠不要压测小弟的服务器,1核、1G、1M的非常脆弱,跪谢!

2.6.1 按连接数限速

按连接数限速是指限制单个IP(或者其他的key)同时发起的连接数,超出这个限制后,Nginx将直接拒绝更多的连接。可以对于一些服务器流量异常、负载过大,甚至是大流量的恶意攻击访问等,进行并发数的限制。
-Nginx配置

http {limit_conn_zone $binary_remote_addr zone=addr:10m;limit_conn_status 429;...server {...location /coon.html {limit_conn addr 1;}
  • 使用limit_conn_zone关键字,我们定义了一个名为addr大小为10MB的共享内存区域(zone),用来存放限速相关的统计信息,限速的key值为二进制的IP地址`$binary_remote_addr。
  • 使用limit_req_status关键字,是更改默认限速后返回的状态码。默认返回的是503(服务暂时不可用)。
  • limit_conn addr 1;允许最大1个并发
  • Jmeter测试: http://nginx.wangdatao.com/conn.html


2.6.2 按请求速率限速

按请求速率限速是指限制单个IP(或者其他的key)发送请求的速率,超出指定速率后,Nginx将直接拒绝更多的请求。采用漏桶算法(leaky bucket)算法实现。
下面简单的实现:

  • Nginx配置:
http {limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;limit_req_status 429;...server {...location /req.html {limit_req zone=mylimit burst=2 nodelay;}error_page 429 /429.html;...
  • 使用limit_req_zone关键字,我们定义了一个名为mylimit大小为10MB的共享内存区域(zone),用来存放限速相关的统计信息,限速的key值为二进制的IP地址$binary_remote_addr,限速上限rate为1r/s(1次每秒);接着我们使用limit_req关键字将上述规则作用到/所有请求上。
  • 使用limit_req_status关键字,是更改默认限速后返回的状态码。默认返回的是503(服务不可用)。
  • burst 可以理解为漏桶的容量,不设置这个值时速度达到rate后就报错,设置后有对应的缓冲。
  • nodelay关键字,因为在”桶“中会缓存一些请求等待处理,这时在用户看来请求的时间就边长了,不是很友好。加上这个关键词就可以理解为:既然能进入桶中,那么这个请求应该就是有效的(超过桶的容量直接就报429错误了),那么就不用排队直接处理就好了。nodelayburst 共同使用时才生效
  • 请求示例:http://nginx.wangdatao.com/req.html
    由于阈值设置的比较低,手点即可触发限流
    成功时:

失败时:

2.6.3 源码解读

大神解读:https://www.cnblogs.com/CarpenterLee/p/8084533.html

限速限流 算法 工具相关推荐

  1. 接口限流算法及解决方案

    参考: 接口限流算法:漏桶算法&令牌桶算法 文章目录 一.限流算法 1. 漏桶算法 2. 令牌桶算法 二.令牌桶算法VS漏桶算法 三.解决方案 1. 使用Guava的RateLimiter进行 ...

  2. 接口限流算法:漏桶算法令牌桶算法

    工作中对外提供的API 接口设计都要考虑限流,如果不考虑限流,会成系统的连锁反应,轻者响应缓慢,重者系统宕机,整个业务线崩溃,如何应对这种情况呢,我们可以对请求进行引流或者直接拒绝等操作,保持系统的可 ...

  3. 搞懂限流算法这一篇就够了 No.154

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 TL;DR(too long don't read) 限流算法:计数器.滑动窗口.漏桶.令牌桶 ...

  4. Java 实现滑动时间窗口限流算法,你见过吗?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | dijia478 来源 | https://w ...

  5. 高并发系统的限流算法与实现

    开发高并发系统时有三把利器用来保护系统:缓存.降级和限流. 缓存:缓存的目的是提升系统访问速度和增大系统处理容量. 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的 ...

  6. 服务器三种常见的限流算法

    服务器三种常见的限流算法 1.计数器算法 2. 滑动窗口 3.令牌桶算法 4.漏桶算法 滑动窗口限流 漏桶限流 令牌桶限流 限流算法总结 单机限流和分布式限流 限流组件 在开发高并发系统时,有三把利器 ...

  7. 限流算法-常见的4种限流算法

    首先我们先来看看什么是限流? 限流是指在系统面临高并发.大流量请求的情况下,限制新的流量对系统的访问,从而保证系统服务的安全性. 另一种解释:在计算机网络中,限流就是控制网络接口发送或接收请求的速率, ...

  8. Java接口限流算法

    0. 前言 常见的限流算法有:令牌桶.漏桶.计数器也可以进行粗暴限流实现. 1. 算法介绍 1.1 令牌桶算法 令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌.令牌桶算法的描述如下: ...

  9. 常见的限流算法与实现

    限流的实现 常见的限流算法: 限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机. 常见的限流算法有三种: 计数器限流(固定窗口) 原理: 时间 ...

最新文章

  1. 大连交通大学计算机网络,计算机网络题库(大连交通大学)chapter5.doc
  2. 剑指offer之把字符串里面空格替换成百分之20[时间复杂度是O(n)]
  3. 把有约束最优化问题转化为无约束最优化问题之罚函数法
  4. always on_Always On可用性组中的自动播种
  5. 移动端压缩并ajax上传图片解决方案
  6. 自学Python6个月,你能找到工作吗?
  7. poj 2833 The Average(堆)
  8. core dump 简介
  9. c语言有开始菜单的flybird,C语言实现Flybird
  10. MATLAB实现 ICA 鸡尾酒会语音分离
  11. 如何清除远程桌面连接记录
  12. QDockWidget增加边框
  13. ps教程300集,让你入门就精通(内附资源)
  14. MAC地址解析-广播地址、组播地址和单播地址的特征
  15. java添加例外_JAVA例外处理
  16. keil中go to definition跳转browser窗口
  17. ADI demo PL工程的编译-以adrv9371x_zc706为例子之使用Cygwin
  18. 浏览器修改主页不失效,打开浏览器弹出首页修改方法
  19. java 串口 dtr rts_【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解
  20. eclipse下载教程

热门文章

  1. linux 查看numa信息,Linux中查看NUMA信息
  2. 人工智能是什么?人工智能行业前景如何
  3. 攻防世界-misc-miss_01
  4. VUE 前端中如何进行组件化开发?
  5. CHECK的简单用法
  6. 185电缆的接法图解_铜芯电线拧接法和捆绑法图解
  7. 小兵大乱斗服务器维修吗,这个游戏有点意思《小兵大乱斗》最新评测来袭
  8. magento|paypal快速结账和结账页开发疑难
  9. 二元选择模型:Probit还是Logit?
  10. Fitbit Flex