网站建设平台安全问题有哪些,视频教程网,用火车采集器发布信息时 如何获取网站栏目id,海尔集团网站的网络营销是什么文章目录前言限流的目标流量统计指标限流设计模式流量计数器模式滑动时间窗模式漏桶模式令牌桶模式分布式限流总结附前言
任何一个系统的运算、存储、网络资源都不是无限的#xff0c;当系统资源不足以支撑外部超过预期的突发流量时#xff0c;就应该要有取舍#xff0c;建…
文章目录前言限流的目标流量统计指标限流设计模式流量计数器模式滑动时间窗模式漏桶模式令牌桶模式分布式限流总结附前言
任何一个系统的运算、存储、网络资源都不是无限的当系统资源不足以支撑外部超过预期的突发流量时就应该要有取舍建立面对超额流量自我保护的机制而这个机制就是微服务中常说的“限流”。
限流的目标
考虑一个简单的场景一个系统的最大处理能力是80TPS在遇到100TPS请求的环境下理想的限流目标是什么 理想的限流目标就是能够完成80TPS的业务只有20TPS的请求失败或被拒绝这可能看上去是理所应当的。但如果未做良好的设计实际场景可能是100TPS涉及的请求都被部分处理实际完成的业务量为0。
所以一个健壮的系统要做到恰当的流量控制需要妥善解决以下三个问题
依据什么限流 要不要控制流量、要控制哪些流量、控制力度要有多大等等这些操作都没法在系统设计阶段静态地给出确定的结论必须根据系统此前一段时间的运行状况甚至未来一段时间的预测情况来动态决定。具体如何限流 要想解决系统具体是如何做到允许一部分请求能够通行而另外一部分流量实行受控制的失败降级的问题就必须要了解和掌握常用的服务限流算法和设计模式。超额流量如何处理 超额流量可以有不同的处理策略也许会直接返回失败如 429 Too Many Requests或者被迫使它们进入降级逻辑这种策略被称为否决式限流也可能是让请求排队等待暂时阻塞一段时间后继续处理这种则被称为阻塞式限流。
流量统计指标
限流中的“流”到底指什么呢要解答这个问题我们得先梳理清楚经常用于衡量服务流量压力但又比较容易混淆的三个指标的定义
每秒事务数Transactions per SecondTPS TPS 是衡量信息系统吞吐量的最终标准。“事务”可以理解为一个逻辑上具备原子性的业务操作或者是需要被看做是原子性的业务操作。比如对一笔订单的支付操作不能允许只支付订单中的包装盒。每秒请求数Hits per SecondHPS HPS 是指每秒从客户端发向服务端的请求数这里要把 Hits 理解为 Requests 而不是 Clicks。如果只要一个请求就能完成一笔业务那 HPS 与 TPS 是等价的。多数情况下一笔业务都伴随多个请求。每秒查询数Queries per SecondQPS QPS 是指一台服务器能够响应的查询次数。如果只有一台服务器来应答请求那 QPS 和 HPS 是等价的但在分布式系统中一个请求的响应往往要由后台多个服务节点共同协作来完成。
在逻辑概念上可以理解为TPS包括了HPS包括了QPS而数值上QPSHPSTPS。
总体来说以上这三点都是基于调用计数的指标而在整体目标上我们当然最希望能够基于 TPS 来限流因为信息系统最终是为人类用户提供服务的用户并不关心业务到底是由多少个请求、多少个后台查询共同协作来实现的。但是因为TPS更接近客户端侧不能准确地反映出服务端系统所承受的压力所以实际操作会比较困难。目前来说主流系统大多倾向于使用 HPS 作为首选的限流指标因为它相对容易观察统计而且能够在一定程度上反映系统当前以及接下来一段时间的压力。 但是限流指标并不存在任何必须遵循的权威法则根据系统的实际需要哪怕完全不选择基于调用计数的指标都是有可能的。举个简单的例子下载、视频、直播等 I/O 密集型系统往往会把每次请求和响应报文的大小作为限流指标。比如说只允许单位时间通过 100MB 的流量再比如网络游戏等基于长连接的应用可能会把登录用户数作为限流指标热门的网游往往超过一定用户数就会让你在登录前排队等候。
限流设计模式
业界内有一些常见、常用、被实践证明有效的设计模式可以参考使用包括流量计数器、滑动时间窗、漏桶和令牌桶这四种。
流量计数器模式
最容易想到的一种做限流的方法就是设置一个计算器根据当前时刻的流量计数结果是否超过阈值来决定是否限流。 这种做法很直观而且有些简单的限流就是这么实现的但它并不严谨如
即使每一秒的统计流量都没有超过 80 TPS也不能说明系统没有遇到过大于 80 TPS 的流量压力。一秒框定的范围内可能超出80TPS但却没被统计器计算到即使连续若干秒的统计流量都超过了 80 TPS也不能说明流量压力就一定超过了系统的承受能力。要看系统的设计比如超时机制
流量计数器模式缺陷的根源在于它只是针对时间点进行离散的统计。因此为了弥补该缺陷一种名为“滑动时间窗”的限流模式就被设计了出来它可以实现平滑的基于时间片段的统计。
滑动时间窗模式
滑动窗口算法Sliding Window Algorithm在计算机科学的很多领域中都有成功的应用比如编译原理中的窥孔优化Peephole Optimization、TCP 协议的阻塞控制Congestion Control等都使用到了滑动窗口算法。而对分布式系统来说无论是服务容错中对服务响应结果的统计还是流量控制中对服务请求数量的统计也都经常要用到滑动窗口算法。
滑动时间窗的工作过程 当频率固定的定时器被唤醒时实际中常用双端队列代替数组
将数组最后一位的元素丢弃掉并把所有元素都后移一位然后在数组第一个插入一个新的空元素。这个步骤即为“滑动窗口”。将计数器中所有统计信息写入到第一位的空元素中。对数组中所有元素进行统计并复位清空计数器数据供下一个统计周期使用。
这种模式也有一些缺点它通常只适用于否决式限流对于超过阈值的流量就必须强制失败或降级很难进行阻塞等待处理也就很难在细粒度上对流量曲线进行整形起不到削峰填谷的作用。
漏桶模式
在计算机网络中专门有一个术语“流量整形”Traffic Shaping用来描述如何限制网络设备的流量突变使得网络报文以比较均匀的速度向外发送。流量整形通常都需要用到缓冲区来实现当报文的发送速度过快时首先在缓冲区中暂存然后在控制算法的调节下均匀地发送这些被缓冲的报文。 这里常用的控制算法有漏桶算法Leaky Bucket Algorithm和令牌桶算法Token Bucket Algorithm两种这两种算法的思路截然相反但达到的效果又是相似的。
针对限流模式的话你可以把“请求”想像成是“水”水来了都先放进池子里水池同时又以额定的速度出水让请求进入系统中。这样如果一段时间内注水过快的话水池还能充当缓冲区让出水口的速度不至于过快。不过由于请求总是有超时时间的所以缓冲区的大小也必须是有限度的当注水速度持续超过出水速度一段时间以后水池终究会被灌满。此时从网络的流量整形的角度看就体现为部分数据包被丢弃而从信息系统的角度看就体现为有部分请求会遭遇失败和降级。
漏桶模式在代码实现上也非常简单它其实就是一个以请求对象作为元素的先入先出队列FIFO Queue队列长度就相当于漏桶的大小当队列已满时就拒绝新的请求进入。漏桶实现起来很容易比较困难的地方只在于如何确定漏桶的两个参数桶的大小和水的流出速率。
首先是桶的大小。如果桶设置得太大那服务依然可能遭遇流量过大的冲击不能完全发挥限流的作用如果设置得太小那很可能就会误杀掉一部分正常的请求这种情况与流量计数器模式中举过的例子是一样的。流出速率在漏桶算法中一般是个固定值这对于固定拓扑结构的服务是很合适的但同时你也应该明白现实世界里系统的处理速度往往会受到其内部拓扑结构变化和动态伸缩的影响。这也暴露出漏桶模式的缺陷。
令牌桶模式
对比来看漏桶是从水池里往系统出水令牌桶则是系统往排队机中放入令牌。
具体实现是假设我们要限制系统在 X 秒内的最大请求次数不超过 Y那我们可以每间隔 X/Y 时间就往桶中放一个令牌当有请求进来时首先要从桶中取得一个准入的令牌然后才能进入系统处理。任何时候一旦请求进入桶中发现没有令牌可取了就应该马上失败或进入服务降级逻辑。 与漏桶类似令牌桶同样有最大容量这意味着当系统比较空闲的时候桶中的令牌累积到一定程度就不再无限增加而预存在桶中的令牌便是请求最大缓冲的余量。
编码实现是
让系统以一个由限流目标决定的速率向桶中注入令牌比如要控制系统的访问不超过 100 次速率即设定为 1/10010 毫秒。桶中最多可以存放 N 个令牌N 的具体数量是由超时时间和服务处理能力共同决定的。如果桶已满第 N1 个进入的令牌就会被丢弃掉。请求到时会先从桶中取走 1 个令牌如果桶已空就进入降级逻辑。
总体来说令牌桶模式的实现看似可能比较复杂每间隔固定时间我们就要把新的令牌放到桶中但其实我们并不需要真的用一个专用线程或者定时器来做这件事情只要在令牌中增加一个时间戳记录每次获取令牌前比较一下时间戳与当前时间就可以轻易计算出这段时间需要放多少令牌进去然后一次性放完全部令牌即可所以真正编码时并不会显得很复杂。是否还应该记录上一次发放令牌的时间戳
分布式限流
此前我们讨论的种种限流算法和模式全部是针对整个系统的限流总是有意无意地假设或默认系统只提供一种业务操作或者所有业务操作的消耗都是等价的并不涉及不同业务请求进入系统的服务集群后分别会调用哪些服务、每个服务节点处理能力有何差别等问题。另外这些限流算法直接使用在单体架构的集群上确实是完全可行的但到了微服务架构下它们就最多只能应用于集群最入口处的网关上对整个服务集群进行流量控制而无法细粒度地管理流量在内部微服务节点中的流转情况。
所以我们把前面介绍的限流模式都统称为单机限流把能够精细控制分布式集群中每个服务消耗量的限流算法称为分布式限流。
分布式限流与单机限流最主要的区别是如何管理限流的统计指标。 单机限流很好办指标都是存储在服务的内存当中而分布式限流的目的是要让各个服务节点的协同限流。无论是将限流功能封装为专门的远程服务还是在系统采用的分布式框架中有专门的限流支持都需要把每个服务节点的内存中的统计数据给开放出来让全局的限流服务可以访问到才行。
一种常见的简单分布式限流方法是将所有服务的统计结果都存入集中式缓存如 Redis中以实现在集群内的共享并通过分布式锁、信号量等机制解决这些数据在读写访问时的并发控制问题。这样在可以共享统计数据的前提下原本用于单机的限流模式理论上也是可以应用于分布式环境中的可是它的代价也显而易见每次服务调用都必须要额外增加一次网络开销所以这种方法的效率肯定是不高的当流量压力大的时候限流本身反倒会显著降低系统的处理能力。只要集中式存储统计信息就不可避免地会产生网络开销。一种改善办法是在令牌桶限流模式的基础上进行“货币化改造”。即不把令牌看作是只有准入和不准入的“通行证”而把它看作是数值形式的“货币额度”。 当请求进入集群时首先在 API 网关处领取到一定数额的“货币”不同等级用户的额度可以有所差异。我们将用户 A 的额度表示为 QuanityA。由于任何一个服务在响应请求时都需要消耗集群中一定量的处理资源所以在访问每个服务时都要求消耗一定量的“货币”。假设服务 X 要消耗的额度表示为 CostX那当用户 A 访问了 N 个服务以后他剩余的额度 LimitN 就会表示为 LimitN QuanityA - ∑NCostX 我们可以把剩余额度 LimitN 作为内部限流的指标规定在任何时候只要剩余额度 LimitN 小于等于 0 时就不再允许访问其他服务了。另外这时还必须先发生一次网络请求重新向令牌桶申请一次额度成功后才能继续访问不成功则进入降级逻辑。除此之外的任何时刻即 LimitN 不为 0 时都无需额外的网络访问因为计算 LimitN 是完全可以在本地完成的。
这种基于额度的限流方案对限流的精确度会有一定的影响比如可能存在业务操作已经进行了一部分服务调用却无法从令牌桶中再获取到新额度因“资金链断裂”而导致业务操作失败的情况。这种失败的代价是比较高昂的它白白浪费了部分已经完成了的服务资源但总体来说它仍然是一种在并发性能和限流效果上都相对折衷可行的分布式限流方案。
总结
对于分布式系统容错的设计是必须要有且无法妥协的措施。但限流与容错不一样做分布式限流从不追求“越彻底越好”我们往往需要权衡方案的代价与收益。
附
此文章为3月Day03学习笔记内容来源于极客时间《周志明的软件架构课》