高并发场景下限流,常见的限流算法、方案解析!

Nginx 如何限流和访问控制(详解) 前言 本文针对 Nginx 的三个模块进行配置,并证实各自的功能特点: limit_conn_zone 模块 - 限制同一 IP 地址并发连接数; limit_request 模块 - 限制同一 IP 某段时间的访问量; core 模块提供 - limit_rate 限制同一 IP 流量。 在 Nginx 中 以 LIMIT 开头的 配置项,都是做 限制 功能,以上三个功能都是 Nginx 编译后就有的功能,属于内置模块。 一、流量控制 1、简介 流量限制 (rate-limiting), 阅读详情

  作者:nick hao

  来源:cnblogs.com/haoxinyue/p/6792309.html

开涛大神在博客中说过:在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。本文结合作者的一些经验介绍限流的相关概念、算法和常规的实现方式。

缓存

缓存比较好理解,在大型高并发系统中,如果没有缓存数据库将分分钟被爆,系统也会瞬间瘫痪。

使用缓存不单单能够提升系统访问速度、提高并发访问量,也是保护数据库、保护系统的有效方式。

大型网站一般主要是“读”,缓存的使用很容易被想到。在大型“写”系统中,缓存也常常扮演者非常重要的角色。

比如累积一些数据批量写入,内存里面的缓存队列(生产消费),以及HBase写数据的机制等等也都是通过缓存提升系统的吞吐量或者实现系统的保护措施。甚至消息中间件,你也可以认为是一种分布式的数据缓存。

降级

服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

降级往往会指定不同的级别,面临不同的异常等级执行不同的处理。

根据服务方式:可以拒接服务,可以延迟服务,也有时候可以随机服务。根据服务范围:可以砍掉某个功能,也可以砍掉某些模块。

总之服务降级需要根据不同的业务需求采用不同的降级策略。主要的目的就是服务虽然有损但是总比没有好。

限流

限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。

一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。

 

限流的算法

常见的限流算法有:计数器、漏桶和令牌桶算法。

计数器

计数器是最简单粗暴的算法。

比如某个服务最多只能每秒钟处理100个请求。我们可以设置一个1秒钟的滑动窗口,窗口中有10个格子,每个格子100毫秒,每100毫秒移动一次,每次移动都需要记录当前服务请求的次数。

内存中需要保存10次的次数。可以用数据结构LinkedList来实现。格子每次移动的时候判断一次,当前访问次数和LinkedList中最后一个相差是否超过100,如果超过就需要限流了。

很明显,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

示例代码如下:

//服务访问次数,可以放在Redis中,实现分布式系统的访问计数
Long counter = 0L;
//使用LinkedList来记录滑动窗口的10个格子。
LinkedList<Long> ll = new LinkedList<Long>();
public static void main(String[] args)
{
  Counter counter = new Counter();
  counter.doCheck();
}
private void doCheck()
{
  while (true)
  {
    ll.addLast(counter);
    if (ll.size() > 10)
    {
      ll.removeFirst();
    }
    //比较最后一个和第一个,两者相差一秒
    if ((ll.peekLast() - ll.peekFirst()) > 100)
    {
      //To limit rate
    }
    Thread.sleep(100);
  }
}

漏桶算法

漏桶算法即leaky bucket是一种非常常用的限流算法,可以用来实现流量整形(Traffic Shaping)和流量控制(Traffic Policing)。

贴了一张维基百科上示意图帮助大家理解:

漏桶算法的主要概念如下:

  • 一个固定容量的漏桶,按照常量固定速率流出水滴;

  • 如果桶是空的,则不需流出水滴;

  • 可以以任意速率流入水滴到漏桶;

  • 如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的。

 

漏桶算法比较好实现,在单机系统中可以使用队列来实现(.Net中TPL DataFlow可以较好的处理类似的问题,你可以在这里找到相关的介绍),在分布式环境中消息中间件或者Redis都是可选的方案。

 

令牌桶算法

令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。

令牌桶算法基本可以用下面的几个概念来描述:

  • 令牌将按照固定的速率被放入令牌桶中。比如每秒放10个。

  • 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝。

  • 当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上。

  • 如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。

如下图:

令牌算法是根据放令牌的速率去控制输出的速率,也就是上图的to network的速率。to network我们可以理解为消息的处理程序,执行某段业务或者调用某个RPC。

 

漏桶和令牌桶的比较

令牌桶可以在运行时控制和调整数据处理的速率,处理某时的突发流量。

放令牌的频率增加可以提升整体数据处理的速度,而通过每次获取令牌的个数增加或者放慢令牌的发放速度和降低整体数据处理速度。而漏桶不行,因为它的流出速率是固定的,程序处理速度也是固定的。

整体而言,令牌桶算法更优,但是实现更为复杂一些。

 

限流算法实现

Guava

Guava是一个Google开源项目,包含了若干被Google的Java项目广泛依赖的核心库,其中的RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

1. 常规速率:

创建一个限流器,设置每秒放置的令牌数:2个。返回的RateLimiter对象可以保证1秒内不会给超过2个令牌,并且是固定速率的放置。

达到平滑输出的效果

public void test()
{
  /**
   * 创建一个限流器,设置每秒放置的令牌数:2个。速率是每秒可以2个的消息。
   * 返回的RateLimiter对象可以保证1秒内不会给超过2个令牌,并且是固定速率的放置。
   * 达到平滑输出的效果
   */
  RateLimiter r = RateLimiter.create(2);
  while (true)
  {
     /**
      * acquire()获取一个令牌,并且返回这个获取这个令牌所需要的时间。
      * 如果桶里没有令牌则等待,直到有令牌。
      * acquire(N)可以获取多个令牌。
      */
      System.out.println(r.acquire());
   }
}

上面代码执行的结果如下图,基本是0.5秒一个数据。拿到令牌后才能处理数据,达到输出数据或者调用接口的平滑效果。

acquire()的返回值是等待令牌的时间,如果需要对某些突发的流量进行处理的话,可以对这个返回值设置一个阈值,根据不同的情况进行处理,比如过期丢弃。

2. 突发流量:

突发流量可以是突发的多,也可以是突发的少。首先来看个突发多的例子。还是上面例子的流量,每秒2个数据令牌。如下代码使用acquire方法,指定参数。

System.out.println(r.acquire(2));
System.out.println(r.acquire(1));
System.out.println(r.acquire(1));
System.out.println(r.acquire(1));

得到如下类似的输出。

 

如果要一次新处理更多的数据,则需要更多的令牌。代码首先获取2个令牌,那么下一个令牌就不是0.5秒之后获得了,还是1秒以后,之后又恢复常规速度。

这是一个突发多的例子,如果是突发没有流量,如下代码:

System.out.println(r.acquire(1));
Thread.sleep(2000);
System.out.println(r.acquire(1));
System.out.println(r.acquire(1));
System.out.println(r.acquire(1));

得到如下类似的结果:

等了两秒钟之后,令牌桶里面就积累了3个令牌,可以连续不花时间的获取出来。处理突发其实也就是在单位时间内输出恒定。

这两种方式都是使用的RateLimiter的子类SmoothBursty。另一个子类是SmoothWarmingUp,它提供的有一定缓冲的流量输出方案。

/**
* 创建一个限流器,设置每秒放置的令牌数:2个。速率是每秒可以210的消息。
* 返回的RateLimiter对象可以保证1秒内不会给超过2个令牌,并且是固定速率的放置。
* 达到平滑输出的效果
* 设置缓冲时间为3秒
*/
RateLimiter r = RateLimiter.create(2,3,TimeUnit.SECONDS);

while (true) {
  /**
   * acquire()获取一个令牌,并且返回这个获取这个令牌所需要的时间。
   * 如果桶里没有令牌则等待,直到有令牌。
   * acquire(N)可以获取多个令牌。
   */
  System.out.println(r.acquire(1));
  System.out.println(r.acquire(1));
  System.out.println(r.acquire(1));
  System.out.println(r.acquire(1));
}

输出结果如下图,由于设置了缓冲的时间是3秒,令牌桶一开始并不会0.5秒给一个消息,而是形成一个平滑线性下降的坡度,频率越来越高,在3秒钟之内达到原本设置的频率,以后就以固定的频率输出。

图中红线圈出来的3次累加起来正好是3秒左右。这种功能适合系统刚启动需要一点时间来“热身”的场景。

Nginx

对于Nginx接入层限流可以使用Nginx自带了两个模块:

  • 连接数限流模块ngx_http_limit_conn_module

  • 漏桶算法实现的请求限流模块 ngx_http_limit_req_module

1.  ngx_http_limit_conn_module

我们经常会遇到这种情况,服务器流量异常,负载过大等等。

对于大流量恶意的攻击访问,会带来带宽的浪费,服务器压力,影响业务,往往考虑对同一个ip的连接数,并发数进行限制。ngx_http_limit_conn_module 模块来实现该需求。

该模块可以根据定义的键来限制每个键值的连接数,如同一个IP来源的连接数。

并不是所有的连接都会被该模块计数,只有那些正在被处理的请求(这些请求的头信息已被完全读入)所在的连接才会被计数。

我们可以在 nginx_conf 的 http{}中加上如下配置实现限制:

#限制每个用户的并发连接数,取名one
limit_conn_zone $binary_remote_addr zone=one:10m;

#配置记录被限流后的日志级别,默认error级别
limit_conn_log_level error;
#配置被限流后返回的状态码,默认返回503
limit_conn_status 503;

然后在 server{}里加上如下代码:

#限制用户并发连接数为1
limit_conn one 1;

然后我们是使用ab测试来模拟并发请求:

ab -n 5 -c 5 http://10.23.22.239/index.html

得到下面的结果,很明显并发被限制住了,超过阈值的都显示503:

另外刚才是配置针对单个IP的并发限制,还是可以针对域名进行并发限制,配置和客户端IP类似。

#http{}段配置
limit_conn_zone $ server_name zone=perserver:10m;
#server{}段配置
limit_conn perserver 1;

2.  ngx_http_limit_req_module

上面我们使用到了ngx_http_limit_conn_module 模块,来限制连接数。那么请求数的限制该怎么做呢?

这就需要通过ngx_http_limit_req_module 模块来实现,该模块可以通过定义的键值来限制请求处理的频率。

特别的,可以限制来自单个IP地址的请求处理频率。限制的方法是使用了漏斗算法,每秒固定处理请求数,推迟过多请求。

如果请求的频率超过了限制域配置的值,请求处理会被延迟或被丢弃,所以所有的请求都是以定义的频率被处理的。

在 http{}中配置

#区域名称为one,大小为10m,平均处理的请求频率不能超过每秒一次。
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

在 server{}中配置

#设置每个IP桶的数量为5
limit_req zone=one burst=5;

上面设置定义了每个IP的请求处理只能限制在每秒1个。并且服务端可以为每个IP缓存5个请求,如果操作了5个请求,请求就会被丢弃。

使用ab测试模拟客户端连续访问10次:

ab -n 10 -c 10 http://10.23.22.239/index.html

如下图,设置了通的个数为5个。一共10个请求,第一个请求马上被处理。第2-6个被存放在桶中。由于桶满了,没有设置nodelay因此,余下的4个请求被丢弃。

【推荐阅读】
2019年最新的常问企业面试题大全以及答案
一套基础自动化部署搭建过程
我是如何用 redis 做实时订阅推送的
为什么很多SpringBoot开发者放弃了Tomcat,选择了Undertow
Java8 的 Stream API 的确牛X,但性能究竟如何呢?

好文!必须点赞
【Gulimall+】秒杀高并发有三宝:缓存、异步、队排好 一提到高并发,秒杀成为了最为典型的业务场景,针对这一特点,必须要做到限流 + 异步 + 缓存(页面静态化) + 独立部署 限流方式: 1、前端限流,一些高并发的网站直接在前端页面开始限流,列如:小米的验证码 2、nginx限流,直接负载部分请求到错误的静态页面,令牌算法、漏斗算法 3、网关限流、限流的过滤器 4、代码中使用分布式信号量 5、rabbitmq限流(能者多劳 channel.basicQos(1))保证发挥服务器的所用性能 ... 阅读详情

相关推荐

【Java核心能力】高并发在简历上如何体现?

个人觉得并发相关的内容是最基础的,同时也是最重要的能力,在面试中并发相关的内容也是比较热点的,在项目中的优化也离不开并发!

qq_45260619的博客 2175

谈谈高并发系统的限流

http://www.cnblogs.com/haoxinyue/p/6792309.html?utm_source=tuicool&utm_medium=referral 开涛大神在博客中说过:在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。本文结合作者的一些经验介绍限流的相关概念、算法和常规的实现方式。 缓存 缓存比较好理解,在大型高并发系统中,如果没有缓存数

java、c++、机器学习方向King 2562

四、高并发系统限流机制——限流算法:令牌桶算法

请求处理时间指系统从接收到请求到处理完成的时间,包括令牌的生成和消耗过程。此指标衡量系统响应速度。系统吞吐量指单位时间内系统能够处理的请求数量。高吞吐量表明系统能在高负载下维持良好性能。延迟指请求从提交到开始处理的时间,包括等待令牌的时间。延迟高时会影响用户体验。

流楚丶格念的博客 1831

WebLogic自保护之一:让WebLogic免于过载之苦

在系统容量达到极限之时,如果中间件仍旧持续接受请求,则必然会导致应用的稳定性和性能急转直下。WebLogic Server从9系起引入了过载保护这一特性,致力于避免业务系统在已经达到容量极限的前提下依然接受客户端请求。本文以WebLogic 10g R3为例,对此特性进行讨论。 过载保护的几种实现手段 Ø       配置线程池,限制请求接入 WebLogic Server从9系

czl8897098的博客 8286

[特殊字符] 深度解析令牌桶算法:从原理到高并发实战(附Java完整实现)

令牌桶算法是分布式系统**流量控制的核心武器**,更是大厂面试必考的高频考点!本文将带你: ✅ 掌握算法底层数学原理 ✅ 手写工业级Java实现代码 ✅ 破解高并发场景实战难题 ✅ 获得大厂级优化方案 文末附【限流算法对比表】+【SpringBoot整合方案】,助你轻松应对亿级流量场景!

m0_51176516的博客 2027

如何在API高并发中玩转资源隔离与限流策略?

title: 如何在API高并发中玩转资源隔离与限流策略?summary:资源隔离是保障API稳定性的核心,通过路由隔离和依赖隔离实现关键业务与非关键业务的分离,避免资源耗尽。限流策略包括令牌桶算法和滑动窗口算法,控制请求速率以保护系统。FastAPI中通过slowapi库实现限流,支持路由级动态限流和分布式限流。分布式限流基于Redis,确保多实例环境下的全局限流。电商支付系统案例展示了核心接口使用独立数据库连接池,日志查询使用公共资源。

https://blog.cmdragon.cn/ 1266

限流算法深度解析与实用指南

在现代软件开发中,服务的高可用性和稳定性是至关重要的,而限流正是确保这一点的有效技术手段之一。限流可以防止过多的请求在短时间内涌向服务,从而引发服务过载并最终导致崩溃。这一部分,我们将探讨限流的必要性、应用场景以及基本原理,为理解接下来的算法和实现策略打好基础。

逆流的小鱼 2411

Sentinel限流黑科技揭秘:3种算法+5大核心组件=如何让服务在高并发下屹立不倒?

本文深入解析Sentinel在分布式系统中的限流机制,从核心组件到算法实现进行全面剖析。文章首先介绍Sentinel的5大核心组件(资源埋点、规则配置中心、统计指标、规则处理器和集群限流),详细阐述其在高并发场景下的作用原理。随后对比4种主流限流算法(固定窗口、滑动窗口、令牌桶和漏桶算法),重点分析滑动窗口算法如何实现毫秒级平滑限流。 通过7个核心组件解析、4种算法对比和12个代码示例,文章揭示了Sentinel在微服务架构中的实战价值,包括其多维度限流能力、动态规则配置优势和集群限流特性。最后总结Sent

java专栏 1854

【限流】4 种常见的限流实现方案

自定义一个限流用的注解,后面在需要限流的方法或接口上面只需添加该注解即可// 限制类型 String limitType();// 每秒 5 个请求 double limitCount() default 5d;// 限制类型 String resourceName();// 每秒 5 个 int limitCount() default 5;

sco5282的博客 5701

【java】高并发之限流 RateLimiter使用

1.概述 转载原文:高并发之限流 你可能知道高并发系统需要限流这个东西,但具体是限制的什么,该如何去做,还是模凌两可。我们接下来系统性的给它归个小类,希望对你有所帮助。 google guava中提供了一个限流实现: RateLimiter,这个类设计的非常精巧,可以适用于我们日常业务中大多数流控的场景,但鉴于使用场景的多样性,使用时也需要相当小心。 前面已经使用两篇简单的文章进行了预热。 信号量限流,高并发场景不得不说的秘密 没有预热,不叫高并发,叫并发高 这次不同。本篇文章将详细的,深入的介绍限流的各.

九师兄 2万+

Java 常用限流算法解析

前言 限流作为高并发场景下抵挡流量洪峰,保护后端服务不被冲垮的一种有效手段,比如大家熟知的限流组件guawa,springcloud中的Hystrix,以及springcloud-alibaba生态中的Sentinel,甚至是基于网关的限流,比如在nginx中配置限流策略,在gateway中配置限流策略等 限流无处不在,既然限流的作用如此强大,那么其底层的实现原理如何呢,说到底,限流的核心是由一系列不同的算法完成,本篇将通过实例来说明下常用的几种限流算法的用法和原理 1、计数器算法 计数器算法限流是采用简单

congge 1万+

高并发场景限流算法

服务器资源的有限性决定了我们无法通过无限制扩容应对流量冲击,而限流的本质正是通过合理的流量管控,在资源约束下最大化保障核心服务的可用性。漏桶算法的核心思想可通过 “进水与出水” 的经典比喻理解:所有用户请求首先进入一个虚拟的 “漏桶”,无论进水速度如何波动(突发或平稳),漏桶都会以固定速率将请求转发至下游服务。这种 “可攒额度” 的特性使其在应对有弹性需求的场景时表现出色,例如 API 网关的流量控制、秒杀活动的流量削峰。当请求量超过漏桶出水速率时,多余的请求会在桶内暂存,若超出桶的容量则直接被丢弃。

srb112233的博客 1229

高并发系统限流代码脚本实战与解析

在某些场景下,我们可能希望更灵活地控制限流逻辑,例如基于 IP、用户 ID 等维度进行限流。此时可以通过编写自定义装饰器实现这一功能。Spring Cloud Gateway 是 Spring Cloud 生态中用于构建 API 网关的核心组件,其内置了强大的限流模块,支持基于 Redis 的分布式限流。@Component// 动态更新逻辑在构建限流监控体系时,需要关注以下几个核心指标:指标名称描述示例值请求总数一段时间内接收到的请求总数10000。

weixin_27645199的博客 1045

解析常见的限流算法

限流技术的核心目标:在保证系统服务质量的前提下,合理控制请求流量,避免资源被过度占用。

Toroto5的博客 1629

小红书UID泄露风险揭秘:如何防止你的手机号被社工库关联(附防护指南)

本文揭示了小红书UID泄露的严重风险,详细解析了UID与手机号关联后被社工库利用的完整链条。通过分析黑产的数据整合技术,提供从基础到进阶的防护策略,包括密码管理、账号隔离和应急响应措施,帮助用户有效防范个人信息泄露风险。

ol789012345的博客 1517

从零开始:在Windows WSL中搭建Openfoam7+blastfoam2.0.0开发环境(避坑大全)

本文详细指导在Windows WSL中搭建OpenFOAM 7与BlastFOAM 2.0.0开发环境,重点解析了安装过程中的常见“坑点”,如编译模式不匹配导致的符号错误,并提供了从系统准备、软件部署到环境验证的完整解决方案,帮助开发者高效构建CFD与爆炸模拟开发平台。

stem5的博客 527

java中常见的限流算法详细解析

突发的流量请求,系统可能会造成奔溃。可以通过集群多个服务器,所以要加以限流 生活中的比如秒杀订单或者微博热搜条等 在某种容器或者验证上也可以加以限流,具体如下 合法性的验证限流:验证码、ip黑名单(防止无限次的调用) 容器限流:tomcat、nginx

码农研究僧的博客 4902

Windows环境下Elasticsearch与JDK版本兼容性实战指南

本文详细解析了Windows环境下Elasticsearch与JDK版本的兼容性问题,提供了从版本匹配检查到安装部署的完整实战指南。重点介绍了Elasticsearch 8.x需JDK 17+等关键版本对应关系,并分享多版本JDK共存方案及性能优化技巧,帮助开发者高效避坑。

weixin_29250403的博客 421

nginx:如何用Nginx做限流,有几种限流算法,分别如何实现

fill:#333;color:#333;color:#333;fill:none;限流算法固定窗口滑动窗口漏桶算法令牌桶自适应限流。

weixin_43290370的博客 2114

YOLO系列算法改进 | C2PSA改进篇 | 融合FSD频域结构解耦模块 | 双域解耦感知,兼顾全局亮度与局部纹理,适应红外小目标与边缘部署场景 | ICME 2026

YOLO系列算法改进 | C2PSA改进篇 | 融合FSD频域结构解耦模块 | 双域解耦感知,兼顾全局亮度与局部纹理,适应红外小目标与边缘部署场景 | ICME 2026 (以YOLO26最新目标检测模型为基础,利用C2PSA_FSD创新模块进行改进)

2502_91881744的博客 316

SpringBoot 系列教程(十二):给大家推荐20个SpringBoot开源项目,私活必备

基于 SpringBoot2.x + Shiro 实现了用户管理、个人微信、支付宝支付,内置扫码支付、后台回调、订单查询、限流等一系列功能

Thinkingcao的专栏 2840
上一篇: 为什么选 RPC 而不是 HTTP ?
下一篇: 项目是如何死掉的?太过真实!
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值