什么是限流
基本概念
在互联网领域,限流是指对进入系统的请求数量或频率进行控制的一种机制,以防止系统因流量暴增而过载,从而保障系统的稳定性和可用性。
限流目的
- 防止系统过载:控制请求速率,避免突发流量导致系统崩溃。
- 资源保护:合理分配系统资源,防止某些请求占用过多资源影响其他请求。
- 防止恶意攻击:通过限制请求频率,抵御DDoS等恶意攻击。
- 保障服务质量:确保系统能为每个请求提供稳定、可靠的服务。
- 削峰填谷:在访问高峰期平滑请求曲线,将超出系统承载能力的请求延后处理或拒绝。
- 成本控制:对于按量计费的云服务,限流可以有效控制成本。
- 公平竞争:确保不同用户或应用程序之间公平地使用系统资源。
应用场景
- 保护后端服务 :在高并发环境下,后端服务可能无法承受大量请求,限流有助于防止服务崩溃。
- 防止 DDoS 攻击 :DDoS 攻击通过大量伪造请求涌入系统,造成系统无法响应。限流可以有效缓解这种 DDoS 攻击。
- 优化用户体验 :通过控制请求频率,避免单个用户或客户端频繁请求系统,从而提升其他用户的访问体验。
- 流量控制 :在某些情况下,系统需要根据当前负载情况动态调整可接受的请求量,避免资源浪费或系统崩溃。
限流算法
- 计数器算法 :通过维护一个计数器来限制在特定时间间隔内的请求数量。该算法在固定时间窗口内对请求进行计数,当请求数超过设定阈值时,则进行限流处理。
- 原理:在固定时间窗口内限制请求数量。
- 优点:实现起来非常简单,逻辑也很直观,易于理解。而且性能高效,因为计数器的操作速度很快,对系统性能的影响很小,内存占用也低,只需维护一个计数器。
- 缺点:
- 缺乏流量整形机制,不能确保请求能够连续、平滑地传递到下游服务。这是因为固定窗口计数器算法的放行速率和流量的涌入速率是相同的,所以在处理不规则或者突发流量时,它的效果就不佳。
- 对突发流量的容忍性也比较差。如果设置的时间窗口是10秒,阈值是100个请求,那在前1秒内涌入100个请求后,接下来的9s不会再放行请求。即使系统可能在第5秒已经处于空闲状态,具备处理新请求的能力,也只能等到下一个限流周期才行。相比之下漏桶、令牌桶则灵活的多,更能容忍突发流量。
- 窗口边界容易出现过载问题。固定窗口算法将时间划分为固定大小的窗口,这种机制在窗口边界可能引发突发请求流量,导致系统的瞬时负载超过预期。比如假设阈值是100个请求,时间窗口是2秒。在当前窗口的最后1秒可能就有100个请求,而在下一个窗口的前1秒又有100个请求。单看窗口,好像限流策略成功,但如果跨越窗口之间来看,实际上在2秒内通过的请求数量可能达到200个,而不是预期的100个。可以用滑动窗口限流算法来解决这个问题。
- 设置恰当的阈值也很难。如果阈值设置得过高,系统可能会允许太多请求在短时间内通过,导致负载过重。而如果设置得过低,系统就会频繁拒绝用户请求,这不仅影响用户体验,还有可能浪费宝贵的系统资源。
- 适用场景:
- 对请求数量要求不严格的简单限流场景。
- 适合资源受限的系统,如内存或处理能力有限的环境。
- 不适用于对突发流量敏感或需要精确控制的场景。
- 滑动窗口算法 :是一种基于时间窗口的限流策略,核心思想是动态跟踪最近一段时间内的请求数量,以适应不均匀的流量,从而有效避免了固定窗口算法所存在的窗口边界问题,同时保持较低的内存开销。
- 原理:将时间窗口细分,动态滑动,提供更平滑的限流效果。
- 优点:解决了固定窗口的窗口边界问题:滑动窗口算法通过持续监控和计算时间段内的请求,减少了由于时间窗口结束而导致的请求突然集中涌入的情况。。
- 缺点:
- 缺乏流量整形机制,无法实现平滑限流:滑动窗口算法没有流量整形机制,无法确保请求连续、平滑地传递到下游服务。由于滑动窗口算法放行的速率与流量涌入的速率相同,它无法有效管理不规则或突发的流量。流量整形是指针对突发流量进行管理,通过预设的速率稳定地输出请求,以确保发送到后端系统的流量保持在可接受的范围内,从而避免对后端造成冲击。
- 难以设置恰当的阀值:如果设置的阀值过高,系统可能会允许过多的请求在短时间内通过,从而导致负载过重。另一方面,如果阀值设置过低,系统将频繁拒绝用户请求,影响用户体验,并浪费宝贵的系统资源。
- 适用场景:
- 对请求数量要求不严格的简单限流场景。
- 适合资源受限的系统,如内存或处理能力有限的环境。
- 不适用于对突发流量敏感或需要精确控制的场景。
- 漏桶算法 :是一种经典的限流算法,它的核心思想是将请求放入一个固定容量的“桶”中,桶以固定的速率“漏水”(即处理请求)。如果桶满了,则新的请求会被拒绝或排队等待。
- 原理:请求先进入桶中,然后以固定速率处理,超出桶容量的请求被丢弃。
- 优点:
- 简单直观:基于桶的模型,算法相对简单,容易实现。
- 平滑限流:能够将突发的高峰流量平滑处理,避免对下游系统的瞬时冲击。
- 缺点:
- 延迟响应请求:在高流量情况下,请求必须排队等待,可能导致用户体验下降。
- 难以设置恰当的限流阀值:如果设置的限流阀值过高,也就是桶的容量过大,那么排队的请求可能超时或者延迟响应,影响用户体验。如果桶的容量过小,那么系统将频繁拒绝用户请求,影响用户体验。
- 难以设置恰当的输出速率: 如果设置的漏水速率过高,可能导致下游服务过载。如果设置的漏水速率过低,漏桶缓存请求增加,漏桶满后会频繁拒绝用户请求,影响用户体验。
- 适用场景:
- API限流:防止短时间内接收到过量请求,保护后端服务。
- 网络流量控制:管理带宽,确保数据传输的稳定性。
- 任务调度:确保任务在执行时保持平稳的速率。
- 高并发请求处理:如高并发的Web应用程序,漏桶算法能够平稳处理大量请求。
- 服务高可用:控制访问第三方服务的速度,防止压垮下游。控制服务自身的处理速度,方式过载。
- 令牌桶算法 :一种基于固定容量桶模型的算法,特点是如果请求进入系统的速率超过令牌的生成速率,或者如果一次进入的请求数量超过桶中的令牌数,则请求被限流。它通过动态限制请求进入系统的速率来实现限流,比如,令牌的生成速率是2个/s,桶的容量是10,那么请求进入系统进入的最大速率是10个/s,平均速率2个/s。
- 原理:令牌桶算法的基本构思是使用一个“桶”,其中存放了“令牌”,每个令牌允许处理一个请求。系统以固定的速率向这个桶中生成令牌,直到达到最大的桶容量。如果桶已满,新生成的令牌将会被丢弃。由于令牌可以在桶中累积,这使得算法在遭遇短时间内的请求高峰时,依旧能够保持一定的处理能力。
- 优点:
- 容忍突发流量:令牌桶算法能够在一定程度上应对突发流量,当请求量增加时,桶中的令牌会被快速消耗,但只要有新的令牌不断被添加,系统就能够持续处理请求。
- 缺点:
- 实现复杂:令牌桶算法的实现和管理需要占用一定的内存和CPU资源。
- 未实现平滑限流:令牌桶算法缺少流量整形机制,如果桶容量设置的不好,高峰流量会对下游系统造成瞬时冲击。
- 难以设置恰当的限流阀值:如果设置的限流阀值过高,也就是桶的容量过大,当桶满时,突发流量都被允许通过,会导致下游系统过载。如果桶的容量过小,那么系统将频繁拒绝用户请求,系统资源未被充分利用,同时也影响用户体验。
- 难以设置恰当的填充速率: 如果设置的令牌生成速率过高,可能导致下游服务过载。如果设置的令牌生成速率过低,会频繁拒绝用户请求,影响用户体验。
- 适用场景:
- API限流:有效控制API的访问速率,防止系统过载。
- 网络流量管理:在网络设备中限制带宽使用,能够容忍网络抖动,保障公平性和服务质量。
- 服务高可用:控制访问第三方服务的速度,防止压垮下游。控制服务自身的处理速度,方式过载。
拒绝策略
当请求超过限流阈值时,系统可以采取不同的拒绝策略:
- 直接拒绝:返回错误码或错误信息(如HTTP 429 Too Many Requests)。
- 丢弃请求:直接丢弃超出的请求,不予处理。
- 延迟处理:将请求放入队列,等待系统有空闲资源时再处理。
- 降级处理:返回降级后的结果,如使用缓存数据而非实时数据。
- 特殊处理:执行特定的代码逻辑,如返回默认值或备选方案。
- 调用方熔断:通知调用方暂停发送请求。
- 局部熔断:对某些高风险或低优先级的功能进行熔断,暂时停止提供服务。
限流策略
限流策略可以从以下维度考虑,每个维度都包含不同的粒度级别:
- 系统架构维度
- 服务端:
- 全局级:整个系统的总体流量限制
- 区域级:针对不同地理区域或数据中心的限流
- 集群级:分布式系统或微服务集群的流量控制
- 单机级:单个服务器节点或实例的限流
- 服务级:特定微服务的流量限制
- 模块级:技术架构层面的模块限流(如数据访问层、业务逻辑层、展示层)
- 接口级:特定API接口的调用频率限制
- 方法级:具体方法或函数的调用频率控制
- 客户端:
- IP级:特定IP地址或IP段的请求限制
- 设备级:不同客户端设备类型(如移动端、PC端)的限流
- 服务端:
- 业务维度
- 产品线级:不同产品线或业务线的限流策略
- 用户账户级:单个用户账户的请求频率限制
- 用户角色级:基于用户角色(如VIP、普通用户)的差异化限流
- 操作类型级:不同操作类型(如读、写、删除)的限流
- 内容类型级:根据处理的内容类型(如文本、图片、视频)设置的限流
- 业务场景级:特定业务场景(如秒杀、大促)的流量控制
- 业务功能级:针对特定业务功能(如订单处理、支付、搜索)的限流
- 资源维度
- 缓存级:缓存系统访问的流量控制
- 数据库级:数据库访问频率的限制
- 第三方服务级:外部服务或API调用的限流
常用限流策略举例:
- 基于用户等级的差异化限流
- 普通用户:每分钟100次API调用
- 银牌用户:每分钟300次API调用
- 金牌用户:每分钟500次API调用
- VIP用户:每分钟1000次API调用
- 基于接口重要性的限流
- 核心业务接口(如下单):每秒1000次
- 非核心业务接口(如用户信息查询):每秒500次
- 管理类接口:每秒100次
- 多维度组合限流
- 单个IP限制:每秒不超过100次请求
- 单个用户限制:每分钟不超过1000次请求
- 单个接口限制:每秒不超过10000次请求
- 整个系统限制:每秒不超过100000次请求
- 时间段差异化限流
- 正常时段(8:00-22:00):系统每秒处理10000次请求
- 低峰时段(22:00-8:00):系统每秒处理5000次请求
- 促销活动时段:系统每秒处理20000次请求
- 基于资源消耗的自适应限流
- 当CPU利用率<50%时,允许每秒10000次请求
- 当CPU利用率在50%-80%时,每秒允许5000次请求
- 当CPU利用率>80%时,每秒允许1000次请求
- 针对特定业务场景的限流
- 注册接口:每个IP每天最多注册5个账号
- 登录接口:每个账号每分钟最多尝试5次登录
- 评论接口:每个用户每分钟最多发布10条评论
- 基于地理位置的限流
- 主要市场(如中国):允许每秒10000次请求
- 次要市场(如东南亚):允许每秒5000次请求
- 其他地区:允许每秒1000次请求
- 渐进式限流策略
- 当请求量达到阈值的80%时,开始对非核心业务限流
- 当请求量达到阈值的90%时,对所有业务实施更严格的限流
- 当请求量达到阈值的100%时,只保留最核心的业务功能
- 基于内容的智能限流
- 对包含敏感词的内容请求进行更严格的限流
- 对高频热门内容的请求实施更宽松的限流策略
- 结合业务目标的限流
- 确保支付相关的API永远有30%的处理能力
- 在促销期间,为商品展示和搜索API预留50%的系统资源
此外,限流策略通常与熔断、降级、负载均衡、缓存等机制结合,构建更健壮的系统,比如:
- 限流与熔断,利用熔断器模式(如使用Hystrix或Resilience4j)与限流配合,当触发限流时,可以同时启动熔断机制,快速失败并保护系统。
- 限流与降级,通过配置中心,灵活调整降级策略和限流阈值。在限流触发时,执行服务降级逻辑,如返回缓存数据或默认值。
- 限流与负载均衡,结合负载均衡策略,将超出限流阈值的请求分发到其他可用节点,从而实现基于限流状态的智能负载均衡,避免单点过载。
- 限流与缓存,利用缓存减少对后端服务的直接请求,间接实现限流效果。在限流触发时,优先返回缓存数据,保证基本的服务可用性。
注意事项
- 实施多级限流:从全局到局部,实施多层次的限流策略。
- 合理设定限流参数:根据系统容量和业务特性设定合理的限流阈值。
- 预留冗余:针对允许一定突发流量的业务,预留适当的冗余容量。
- 监控和报警:实时监控限流情况,设置报警机制及时发现问题。
- 动态调整:根据业务变化和系统性能,定期审查和调整限流策略。
- 确保高可用性:对限流组件进行冗余设计,防止单点故障。
- 优化用户体验:提供友好的错误提示,引导用户采取适当行动。
- 定期测试和演练:模拟高并发和异常情况,验证限流策略的有效性。
- 结合业务场景:针对不同的业务场景和优先级,制定差异化的限流策略。
- 日志记录:详细记录限流触发的原因、时间和影响,便于后续分析和优化。
限流是构建高可用、高性能系统的关键技术之一。它通过控制请求速率,有效地保护系统免受过载和恶意攻击的影响。在实际应用中,需要根据具体的业务场景和系统架构,选择合适的限流算法和实现方案。同时,将限流与其他系统保护机制(如熔断、降级)结合使用,可以构建更加健壮和可靠的系统。
面试技巧
15K 回答
(综述)限流是一种非常重要的系统保护机制。它的主要作用是控制系统处理请求的速度,特别是在面对高并发或者资源紧张的情况下。通过限制请求流量,可以有效防止系统过载,保证服务的稳定性和可用性。我个人认为,限流就像是系统的一道防护墙,通过牺牲一部分请求来确保整个系统的正常运行。
(限流目的)说到限流的目的,主要有几个方面:首先是防止系统过载,控制请求速率可以避免突发流量导致系统崩溃。其次是保护系统资源,防止某些请求占用太多资源影响其他请求。还有就是防止恶意攻击,比如DDoS攻击,通过限制请求频率可以起到一定的防御作用。另外,限流还能保障服务质量,确保每个请求都能得到稳定、可靠的服务。在访问高峰期,限流可以帮助我们平滑请求曲线,把超出系统承载能力的请求延后处理或者拒绝掉。对于一些按量计费的云服务,限流还能帮助我们控制成本。最后,限流还能确保不同用户或应用程序之间公平地使用系统资源。
(算法与优缺点)常见的限流算法主要静态和动态算法两类。静态算法主要有四种:固定窗口计数器、滑动窗口计数器、漏桶算法和令牌桶算法。每种算法都有自己的特点和适用场景。比如固定窗口计数器实现简单,性能高,但可能在窗口边界出现突发流量。滑动窗口计数器则能提供更平滑的限流效果,但实现起来稍微复杂一些。漏桶算法适合平滑输出流量,但对突发请求的处理不太友好。令牌桶算法则更灵活,允许一定的突发请求,但需要精确的时间控制。动态算法通常是在静态算法的基础上,根据系统的实时负载状况、关键指标或者优先级来动态调整限流策略。这种方法更加灵活,能够更好地适应系统的实际情况。
(拒绝策略)当请求超过限流阈值时,通常需要相应的拒绝策略。常见的策略包括直接拒绝并返回错误信息,或者将请求放入队列延迟处理,还可以返回降级后的结果,比如使用缓存数据。在一些特殊情况下,我们可能需要执行一些特定的代码逻辑,或者通知调用方暂停发送请求。
(限流维度)在实际应用中,限流策略可以从多个维度考虑,比如系统架构维度、业务维度和资源维度。每个维度都包含不同的粒度级别,比如:从系统架构维度来看,有全局限流、区域限流、集群限流、单机限流等等;从业务维度来看,有用户限流,特定业务限流;从资源维度来看,有对缓存、数据库、第三方服务等资源的访问限流。
此外,限流通常还需要与其他机制结合使用,比如熔断、降级、负载均衡等,以构建更加健壮的系统。举个例子来说,在使用单机限流的情况下,负载均衡器可以在收到限流错误之后,选择另外一个节点重试。
总的来说,限流需要根据具体的业务场景和系统架构来设计并实现。通过合理的限流策略,我们可以有效地提高系统的稳定性和可靠性。
引导点:
限流算法;动态限流算法;拒绝策略;限流结合负载均衡;
25K 回答
在我的实际工作经验中,我使用过以下限流策略:
比如说在 Redis 作为缓存的架构中,当请求达到限流阈值时,可以让这些被限流的请求只访问 Redis,而不再访问后端的主数据库或其他较重的计算逻辑。当然,正常的请求还是走 Redis、数据库这条路。
这种策略的优点是可以大大减轻后端系统的压力,同时还能保证用户快速获得响应。只有恰好 Redis 中没有缓存数据,并且这个请求又是触发了限流的情况,用户会拿不到数据。但是可以预期没有被限流的请求会把缓存重新加载好,保证用户体验。
而在一些可以接受延迟处理的写操作或者复杂计算场景中。当系统达到限流阈值时,我们不会直接拒绝这些请求,而是将它们放入消息队列中,等待系统闲时再进行处理。比如说在通知服务里面,收到发送短信邮件之类的请求的时候,如果此时系统已经负担很重了,那么系统会直接返回已接收的响应,但是其实请求转发到了 Kafka 上。
这种策略的好处是可以削峰填谷,有效地平衡系统负载。对于用户来说,他们的请求没有被直接拒绝,只是处理时间可能会稍长一些。我们通常会给用户一个提示,告诉他们请求已经收到,正在处理中。
在实施这种策略时,我们需要考虑几个关键点:首先是队列的容量控制,防止队列过长导致内存问题;其次是要有合适的消费策略,确保队列中的请求能够及时处理;最后,对于一些有时效性的操作,我们还需要设置过期机制,避免处理已经失去意义的请求。
速记点:
被限流只查询Redis;被限流转异步到 Kafka 上;
35K 回答
从服务治理的角度来说,熔断、限流和降级之间其实并没有什么本质区别。
这三者都可以归类为故障检测-故障处理-故障恢复这一个框架之下。比如说动态限流算法同样可以用于熔断和降级。例如说最简单的内存使用率过高就限流,自然也是可以降级,也可以熔断。
只是说,限流强调的是部分请求还能正常被处理,熔断强调的是防止故障扩散,而降级则是提供有损服务。
引导点:
熔断;降级;
速记点:
熔断、限流和降级,本质一样;
亮 点:
故障检测-故障处理-故障恢复框架