接口高可用设计 - 限流
限流的概念
限流是一种常用的系统保护和负载控制手段,主要目的是通过控制系统处理请求的速率,来防止系统过载,并保持系统稳定运行。如果没有限流措施,当流量激增时,系统可能会因为资源短缺而无法正常处理所有请求,从而导致性能急剧下降甚至崩溃。
请求端限流:请求端限流通常在客户端进行,即在发起请求时就进行限流。这种方式的好处是可以在早期阶段就控制请求的流量,避免大量无效的请求占用网络资源,同时也可以减少服务端的压力。但是,请求端限流需要客户端的配合,如果客户端没有正确地实施限流策略,那么这种方式可能会失效。
接入端限流(分布式限流):接入端限流则是在服务器接收到请求时进行。这种方式通常在负载均衡器、API网关或者反向代理等处实施,通过控制每秒接受的请求数量,来保持后端服务的稳定运行。接入端限流可以在请求进入业务处理流程之前就对其进行控制,既可以防止后端服务过载,也可以在一定程度上防止恶意攻击。
服务端限流(过载保护):服务端限流是在服务处理请求时进行的。这种方式是服务的自我保护机制,当服务检测到自身的处理能力已经达到上限时,会开始丢弃新的请求,从而避免自身过载。服务端限流通常需要对服务的性能有深入的了解,以便在不影响服务正常功能的前提下进行有效的限流。
总结来说,限流是一种重要的系统保护手段,无论是在请求端、接入端还是服务端,都可以通过有效的限流策略,保持系统的稳定运行,提高系统的可用性。
限流具体实现方式
1. 请求端限流
- 常见手段:
- 限制请求次数,例如使按钮变灰,阻止用户进行频繁的操作。
- 嵌入简单的业务逻辑,例如生成随机数来决定是否允许请求。
- 优缺点:
- 优点是实现简单,流量可以在本地直接控制住,不会对服务器产生过多的压力。
- 缺点是这种方式防止的是一般用户的过度请求,但对于使用脚本进行操作的恶意用户,这种方式可能无法有效防止。
2. 接入端限流
- 常见手段:
- 限制同一用户的请求频率,通过用户的唯一标识(如IP地址或用户ID)来限制用户在一段时间内的请求次数。
- 随机抛弃无状态请求,例如对于浏览页面的请求进行限流,而不限制下单请求。
- 优缺点:
- 优点是这种方式可以防止恶意的刷单等行为,为正常用户提供更好的服务。
- 缺点是实现相对复杂,且限流的阈值可能需要人工进行判断和配置,需要考虑到用户体验和服务器负载的平衡。
3. 服务限流
- 常见手段:根据服务的处理能力,丢弃无法处理的请求。当服务器的负载达到一定阈值时,会拒绝部分新的请求,保证服务的正常运行。
- 优缺点:
- 优点是实现简单,可以保护服务器不被过多的请求拖垮。
- 缺点是服务器的处理能力往往难以精准评估和配置,可能会有误判,导致本可以处理的请求被误丢弃。
限流算法
固定窗口算法(Fixed Window Algorithm)
基于计数的限流算法是一种常见的限流算法,其核心是通过维护一个计数器来统计在一个固定时间窗口内的请求量,如果请求量超过了预设的阈值,那么就进行限流。
例如,如果我们设置每分钟只处理100个请求,那么当在一分钟内的请求超过100个时,就拒绝或延迟处理更多的请求。
优点:
- 实现简单:这种限流算法实现起来比较简单,只需要一个计数器和一个定时器就可以。
- 效率高:因为只涉及到简单的计数操作,所以执行效率很高。
缺点:
- 流量统计不精确:即使每一秒的统计流量都没有超过阈值,也不能保证系统在某个小于一秒的时间段内没有承受过超过阈值的流量压力。例如,两个蓝点之间的流量可能会在一个统计周期的短时间内达到峰值,超过系统能承受的最大流量,而在整个统计周期内的平均流量却没有超过阈值。
- 可能导致不必要的请求失败:考虑这样一个场景,如果在10秒的时间片段中,前3秒的请求速率(TPS)平均值达到了150,而接下来的7秒的平均值是30。尽管有一段时间请求速率超过了阈值100,但在10秒的总体时间里,系统仍然有能力处理这些请求而不会导致超时。这是因为在给定的10秒超时时间内,即使是请求速率最高的时段,也可以在大约8秒的时间里处理完。然而,如果我们仅仅基于每秒100个请求的固定时间周期来进行限流,可能会无端地阻止一部分请求,导致部分合理的请求无法被处理,这是没有必要的。这个例子揭示了简单基于计数的限流算法可能的不足,即它可能无法准确反映出系统的实际处理能力。
滑动窗口算法(Sliding Window Algorithm)
滑动窗口算法是一种常用的计算机科学算法,它在许多领域都有成功的应用,例如编译原理中的窥孔优化,TCP 协议的阻塞控制等。在限流场景中,滑动窗口算法通过设定一个特定的时间间隔,即"窗口",在每个窗口期间,系统会记录请求次数,并将其与设定的阈值进行比较。如果请求次数超过阈值,系统将阻止新的请求进入。
例如 每 15 秒钟一个时间窗口。在每个时间窗口内,记录此期间内的请求次数,并将其与特定限流阈值(例如每秒最多处理 100 个请求)进行比较。当请求次数超过阈值时,就禁止进一步的请求进入并触发限制。
优点:
- 它可以实时控制请求次数,防止超过设定的阈值。这意味着它可以避免基于计数的限流算法的流量不均问题。
- 它在单机限流或者分布式服务单点网关中广泛使用。
缺点:
- 它只适用于否决式的限流,也就是说,一旦超过阈值,就必须对流量进行强制失败或降级处理,而不能进行阻塞等待处理。
- 它在细粒度上难以对流量曲线进行整形,也就是说,它不能实现削峰填谷的效果,这可能对某些需要平滑流量的场景造成问题。
漏桶算法(Leaky Bucket Algorithm)
漏桶算法是一种流量整形或速率限制算法,它模拟一个漏洞固定的桶处理输入流。具体来说,请求(或数据包)作为水进入这个桶,然后通过漏洞(即处理单元如线程、进程或服务)以固定的速率离开。
优点:
- 对流量进行平滑处理,减少流量突发。
- 在面临突发流量时,相比其他限流算法可能丢弃较少的请求。
- 可以处理瞬时高并发流量的情况,例如整点秒杀或0点签到。
缺点:
- 桶的大小(即缓冲区大小或请求的最大数量)一旦设置,就难以动态调整。例如,在Java的BlockingQueue中,队列的容量是在初始化时设定的,之后就不能更改。
- 无法控制数据流出(即请求处理)的速度。漏桶算法只能以固定的速率处理请求,无法对处理速度进行动态调整。
- 如果输入流持续大于输出流,那么桶会很快满,后来的请求将被丢弃,可能会造成请求丢失。
令牌桶算法(Token Bucket Algorithm)
令牌桶算法是一种在网络中使用的流量整形和流量控制方法。
基本原理:处理单元以固定的速率生成令牌,并将这些令牌放入“桶”中。当有新的请求到来时,请求需要从桶中获取一个令牌。如果令牌不足,则请求将被拒绝或等待。在某些实现中,如果“桶”已满,新生成的令牌将被丢弃。
技术本质:令牌桶算法是一种速率控制方法。它允许突发数据到达某种程度,因为“桶”中可以积累一定数量的令牌。
优点:
- 可以动态调整处理速度。通过改变生成令牌的速率,可以灵活地控制流量。
- 由于“桶”可以存储一定数量的令牌,因此在一定程度上可以容忍突发流量。
缺点:
- 在突发流量时可能会拒绝大量的请求,因为令牌数量是有限的。
- 实现相对复杂。需要维护令牌的生成和消耗,以及在令牌不足时的请求处理。
应用场景:
- 控制访问第三方服务的速度。例如,第三方服务可能会对访问频率进行限制,可以使用令牌桶算法来确保不超过这个限制。
- 控制自己的处理速度。例如,如果处理请求需要消耗一定的资源(如CPU或内存),可以使用令牌桶算法来确保处理速度不超过资源的可用性。
总结
限流是一种保护服务稳定的关键策略,通过控制流入系统的请求量来防止系统超负荷运行。本文主要讨论了四种主要的限流算法:固定窗口算法、滑动窗口算法、漏桶算法和令牌桶算法,对比了它们的原理、优缺点、使用场景等。
以下是对比表格:
限流算法 | 原理 | 优点 | 缺点 | 使用场景 | 流量整形 | 处理延时 |
---|---|---|---|---|---|---|
固定窗口 | 在固定的时间窗口内计数并进行限流 | 实现简单,效率高 | 流量可能突增,无法均匀分配请求 | 请求分布不敏感的服务 | 否 | 否 |
滑动窗口 | 窗口在时间轴上滑动,记录请求次数 | 实时控制,流量较均匀 | 难以实现流量曲线整形,无法削峰填谷 | 单机或单点网关限流 | 否 | 否 |
漏桶 | 流入请求如水流入桶,满则丢弃 | 能够控制突发流量 | 桶大小调整困难,无法控制处理速度 | 瞬时高并发流量场景 | 是 | 是 |
令牌桶 | 以一定速率产生令牌,请求需拿令牌 | 可动态调整处理速度 | 实现复杂,突发流量可能导致大量丢弃请求 | 控制访问第三方服务速度 | 是 | 是 |