高并发场景下的限流策略

高并发场景下的限流策略:

  在开发高并发系统时,有很多手段来保护系统:缓存、降级、限流。

  当访问量快速增长、服务可能会出现一些问题(响应超时),或者会存在非核心服务影响到核心流程的性能时, 仍然需要保证服务的可用性,即便是有损服务。所以意味着我们在设计服务的时候,需要一些手段或者关键数据进行自动降级,或者配置人工降级的开关。

缓存的目的是提升系统访问速度和增大系统处理的容量,可以说是抗高并发流量的银弹;降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉某些功能,等高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如秒杀、抢购;写服务(评论、下单)、频繁的复杂查询,因此需要一种手段来限制这些场景的并发/请求量性能调优是针对于代码本身的不规范性和系统资源的瓶颈的,当计算机的硬件资源达到瓶颈的时间已经无法调优了。高并发场景下一方面通过缓存,异步化,服务化,集群去增加整个系统的吞吐量,另一方面通过限流,降级来保护系统。

限流的作用:

  在各大节假日旅游高峰期,各大旅游景点都是人满为患。所有有些景点为了避免出现踩踏事故,会采取限流措施。那在架构场景中,是不是也能这么做呢?针对这个场景,能不能够设置一个最大的流量限制,如果超过这个流量,我们就拒绝提供服务,从而使得我们的服务不会挂掉。当然,限流虽然能够保护系统不被压垮,但是对于被限流的用户,就会很不开心。所以限流其实是一种有损的解决方案。但是相比于全部不可用,有损服务是最好的一种解决办法。

  限流的设计还能防止恶意请求流量、恶意攻击。所以,限流的基本原理是通过对并发访问/请求进行限速或者一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或者告知资源没有了)、排队或等待(秒杀、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)一般互联网企业常见的限流有:

  • 限制总并发数(如数据库连接池、线程池)
  • 限制瞬时并发数(nginx的limit_conn模块,用来限制瞬时并发连接数)
  • 限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
  • 其他的还有限制远程接口调用速率、限制MQ的消费速率。
  • 另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。

  有了限流,就意味着在处理高并发的时候多了一种保护机制,不用担心瞬间流量导致系统挂掉或雪崩,最终做到有损服务而不是不服务;但是限流需要评估好,不能乱用,否则一些正常流量出现一些奇怪的问题而导致用户体验很差造成用户流失。

常见的限流算法:

  常见的限流算法有:滑动窗口、令牌桶、漏桶。计数器也可以进行粗暴限流实现。

滑动窗口协议:

  是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,同时控制流量也可以避免网络拥塞。

  下面图中的0,1,2,3,4号数据帧已经被发送出去,但是1,2,3,4未收到关联的ACK,6,7,8....帧则是等待发送,由于0号数据帧已经接收到服务端的ACK,导致了窗口滑动,这里5号数据帧处于可发送状态。可以看出发送端的窗口大小为5,这是由接受端告知的。此时如果发送端收到1号ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”,即数据帧6也可以被发送。Alibaba Sentinel 中间件就使用了该协议。

  滑动窗口协议动画演示地址:https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html

漏桶算法:(控制传输速率Leaky bucket)

  漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

  漏桶算法思路是,不断的往桶里面注水,无论注水的速度是大还是小,水都是按固定的速率往外漏水;如果桶满了,水会溢出;桶本身具有一个恒定的速率往下漏水,而上方时快时慢的会有水进入桶内。当桶还未满时,上方的水可以加入。一旦水满,上方的水就无法加入。桶满正是算法中的一个关键的触发条件(即流量异常判断成立的条件)。而此条件下如何处理上方流下来的水,有两种方式,在桶满水之后,常见的两种处理方式为:

  1. 暂时拦截住上方水的向下流动,等待桶中的一部分水漏走后,再放行上方水。
  2. 溢出的上方水直接抛弃。

令牌桶(能够解决突发流量):

  令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。令牌桶是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌; 令牌桶算法实际上由三部分组成:两个流和一个桶,分别是令牌流、数据流和令牌桶

令牌流与令牌桶

  系统会以一定的速度生成令牌,并将其放置到令牌桶中,可以将令牌桶想象成一个缓冲区(可以用队列这种数据结构来实现),当缓冲区填满的时候,新生成的令牌会被扔掉。这里有两个变量很重要:第一个是生成令牌的速度,一般称为 rate 。比如,我们设定 rate = 2 ,即每秒钟生成 2 个令牌,也就是每 1/2 秒生成一个令牌;第二个是令牌桶的大小,一般称为 burst 。比如,我们设定 burst = 10 ,即令牌桶最大只能容纳 10 个令牌。

有以下三种情形可能发生:

  1. 数据流的速率 等于 令牌流的速率。这种情况下,每个到来的数据包或者请求都能对应一个令牌,然后无延迟地通过队列;
  2. 数据流的速率 小于 令牌流的速率。通过队列的数据包或者请求只消耗了一部分令牌,剩下的令牌会在令牌桶里积累下来,直到桶被装满。剩下的令牌可以在突发请求的时候消耗掉。
  3. 数据流的速率 大于 令牌流的速率。这意味着桶里的令牌很快就会被耗尽。导致服务中断一段时间,如果数据包或者请求持续到来,将发生丢包或者拒绝响应。

  

原文地址:https://www.cnblogs.com/wuzhenzhao/p/11393575.html

时间: 2024-10-11 04:12:10

高并发场景下的限流策略的相关文章

慌了,大厂最后一面居然问我高并发系统下的限流?

开涛大神在博客中说过:在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.本文结合作者的一些经验介绍限流的相关概念.算法和常规的实现方式. 缓存 缓存比较好理解,在大型高并发系统中,如果没有缓存数据库将分分钟被爆,系统也会瞬间瘫痪.使用缓存不单单能够提升系统访问速度.提高并发访问量,也是保护数据库.保护系统的有效方式.大型网站一般主要是"读",缓存的使用很容易被想到.在大型"写"系统中,缓存也常常扮演者非常重要的角色.比如累积一些数据批量写入,内存里面的缓存

缓存在高并发场景下的常见问题

缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象.这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 “雪崩”现象.此外,当某个缓存key在被更新时,同时也可能被大量请求在获取,

高并发场景下的缓存有哪些常见的问题?

一.缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象. 这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 二.缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 "雪崩"现象. 此外,当某个缓存key在被更新时,同时也

高并发场景下使用缓存需要注意那些问题?

一.缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象.这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 二.缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 "雪崩"现象.此外,当某个缓存key在被更新时,同时也可能

【转】记录PHP、MySQL在高并发场景下产生的一次事故

看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且直觉告诉我们,这是不可能的,就一直没有在意,直到后来真的发现了一个用户确实是42亿,当时我们整个公司都震惊了,如果有大量用户是这样的情况,公司要亏损几十万,我们的老大告诉我们,肯定是什么地方数据溢出的,最后我们一帮人,疯了似的查代码,发现…… 如果按照正常的程序逻辑走下去,代码是完全没问题,但是我发

高并发场景下秒杀项目静态锁的使用疑问

题:高并发场景下秒杀项目静态锁的使用疑问场景:我们有一个秒杀平台,可以提供所有接入公司创建的秒杀活动,简单描述如下:1.秒杀10袋洗衣粉,开始时间12:00(项目ID:A001)2.秒杀iPhone5,开始时间12:00(项目ID:A002)3.秒杀水杯,开始时间12:00(项目ID:A003)... ...(项目ID:A004-A009)10.秒杀ThinkPad,开始时间12:00(项目ID:A010) 例如上面,同时有十个秒杀,都是12:00整开始,每个秒杀之间没有任何关系. 按照我之前的

高并发场景下System.currentTimeMillis()的性能问题的优化

前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法,有时不得不使用,比如生成wokerId.打印日志什么的,在高并发情形下肯定存在性能问题的,但怎么做才好呢? System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道.那什么快?内存!如果该方法从内存直接取数,那不就美滋滋了. 代码实现 package com.nyvi.support.uti

高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; /** * 高并发场景下System.currentTimeMillis()的性能问题的优化 * <p><p> * System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100

高并发场景下请求合并的实践

前言 项目中一般会请求第三方的接口,也会对外提供接口,可能是RPC,也可能是HTTP等方式.在对外提供接口时,有必要提供相应的批量接口,好的批量实现能够提升性能. 高并发场景中,调用批量接口相比调用非批量接口有更大的性能优势.但有时候,请求更多的是单个接口,不能够直接调用批量接口,如果这个接口是高频接口,对其做请求合并就很有必要了.比如电影网站的获取电影详情接口,APP的一次请求是单个接口调用,用户量少的时候请求也不多,完全没问题:但同一时刻往往有大量用户访问电影详情,是个高并发的高频接口,如果