零、文章目录
架构08-流量治理
1、服务面临挑战及解决方案
- 面临的挑战
- **雪崩效应:**一个服务的崩溃可能导致所有依赖该服务的其他服务也无法正常工作,错误层层传递,最终影响整个调用链。
- **处理能力不足:**服务在面对突发请求时,由于处理能力有限,大部分请求可能超时,导致类似交通堵塞的情况,系统恢复时间较长。
- 解决方案
- **服务容错:**通过各种容错策略确保服务在故障时仍能继续运行。
- **流量控制:**限制请求流量,防止系统过载。
- **服务质量管理:**监控和管理服务的质量,确保系统稳定运行。
2、服务容错
(1)什么是容错性设计
- **定义:**容错性设计(Design for Failure)是微服务架构的核心原则之一,旨在确保系统在部分组件出现故障时仍能继续运行。
- **重要性:**分布式系统的本质是不可靠的,容易出现程序崩溃、节点宕机、网络中断等问题。因此,容错性设计对于保证系统的可用性和稳定性至关重要。
(2)常见的容错策略
- 故障转移(Failover)
- **定义:**当调用的服务器出现故障时,系统自动切换到其他服务副本,尝试其他副本能否返回成功调用的结果。
- **优点:**提高系统的高可用性。
- **缺点:**重试有执行成本,过度重试可能使系统更不稳定。
- **应用场景:**关键路径上的服务,需要高可用性的场景。
- 快速失败(Failfast)
- **定义:**在某些业务场景中,不允许做故障转移,因为服务不具备幂等性,重复调用可能产生脏数据。
- **优点:**避免重复调用带来的副作用。
- **缺点:**可能导致服务调用失败。
- **应用场景:**支付场景等需要确保数据一致性的场景。
- 安全失败(Failsafe)
- **定义:**对于旁路逻辑,即使调用失败也当作成功返回,并记录日志。
- **优点:**不影响核心业务的正确性。
- **缺点:**可能掩盖一些潜在问题。
- **应用场景:**审计、日志记录等旁路逻辑。
- 沉默失败(Failsilent)
- **定义:**当请求失败后,默认服务提供者在一段时间内无法提供服务,不再向其分配请求流量。
- **优点:**避免资源浪费,减少对系统的影响。
- **缺点:**可能导致某些请求永久失败。
- **应用场景:**大量请求需要处理的场景。
- 故障恢复(Failback)
- **定义:**将失败的调用信息存入消息队列,由系统自动异步重试调用。
- **优点:**尽力促使失败的调用最终成功。
- **缺点:**增加了系统复杂性。
- **应用场景:**对实时性要求不高的场景。
- 并行调用(Forking)
- **定义:**同时向多个服务副本发起调用,只要有其中一个返回成功即宣告成功。
- **优点:**提高执行时间和成功概率。
- **缺点:**增加了执行成本。
- **应用场景:**关键场景,需要高可靠性的场景。
- 广播调用(Broadcast)
- **定义:**同时向多个服务副本发起调用,要求所有请求全部成功才算是成功。
- **优点:**确保所有服务提供者都更新。
- **缺点:**任何一个服务提供者失败即宣告失败。
- **应用场景:**刷新分布式缓存等需要一致性更新的场景。
(3)服务熔断与服务降级
- **服务熔断:**自动进行服务熔断,属于快速失败策略的实现方法。
- **服务降级:**在调用失败后,主动处理调用失败的后果,如记录业务日志、提供备用服务等。
(4)服务容错设计模式
- 断路器模式
- **定义:**通过代理(断路器对象)接管服务调用者的远程请求,监控服务返回的结果。
- 状态:
- **CLOSED:**正常调用远程服务。
- **OPEN:**直接返回失败信息,不进行远程调用。
- **HALF OPEN:**放行一次远程调用,根据结果决定下一步状态。
- **作用:**避免雪崩效应,实现快速失败策略。
- 舱壁隔离模式
- **问题:**由于某个外部服务导致的阻塞
- **定义:**通过独立的线程池或信号量机制控制单个服务的最大连接数,避免某个服务的故障影响全局。
- 实现:
- **线程池:**为每个服务单独设立线程池。
- **信号量:**维护一个线程安全的计数器,控制并发调用的最大次数。
- **作用:**实现静默失败策略,隔离故障影响。
- 应用:
- **微观层面:**控制单个服务的最大连接数,隔离故障影响。
- **宏观层面:**按功能、子系统、用户类型等条件隔离资源,控制波及范围。
- 重试模式
- **定义:**对瞬时故障进行重试,尝试恢复服务。
- 条件:
- 仅在关键服务上进行同步重试。
- 仅对瞬时故障导致的失败进行重试。
- 仅对具备幂等性的服务进行重试。
- 重试必须有明确的终止条件(超时或次数)。
- **作用:**解决瞬时故障,提高服务的可靠性。
- 风险与优化
- **风险:**滥用重试可能导致系统负担加重。
- **优化:**合理设置重试条件和终止条件,避免过度重试。
(5)未来发展方向
- **动态调整:**根据服务负载和调用统计结果自动调整容错策略和参数。
- **启发式搜索:**通过算法自动优化容错策略。
3、限流控制
(1)限流的概念与目标
- **限流:**分布式服务中常见的机制,旨在保护系统免受超出预期的突发流量冲击。任何系统的运算、存储、网络资源都是有限的,当资源不足以支撑外部流量时,系统需要有取舍,建立自我保护机制。
- 目标:
- **单个用户访问:**完成一次业务操作所需系统处理器时间。
- **集群每秒最大处理能力:**集群中每个服务每秒最大能处理的请求数。
- **集群最大业务操作能力:**在不超时的前提下,整个集群每秒能处理的业务操作数。
- **超额流量处理:**当系统接收到超过最大处理能力的请求时,如何处理这些超额请求。
(2)流量统计指标
- **每秒事务数(TPS):**衡量信息系统吞吐量的最终标准,表示每秒完成的业务操作数。
- **每秒请求数(HPS):**每秒从客户端发向服务端的请求数。
- **每秒查询数(QPS):**一台服务器能够响应的查询次数。
(3)限流设计模式
- 流量计数器模式
- **优点:**简单直观。
- **缺点:**离散统计,可能错过瞬时高峰流量,误判流量压力。
- 滑动时间窗模式
- **优点:**平滑统计,能准确反映任意时间片段内的流量压力。
- 缺点:仅适用于否决式限流 ,难以进行流量整形。
- 漏桶模式
- **优点:**简单实现,适用于流量整形。
- **缺点:**缓冲区大小和流出速率的确定较为困难。
- 令牌桶模式
- **优点:**支持变动请求处理速率,适用于流量整形。
- **缺点:**实现相对复杂,需要管理令牌的生成和消耗。
(4)分布式限流
- **单机限流:**统计指标存储在服务的内存中,适用于单体架构。
- **分布式限流:**统计指标需要在集群内共享,通常使用集中式缓存(如 Redis)来实现。
- **基于额度的限流方案:**将令牌看作"货币额度",在请求进入集群时分配额度,访问服务时消耗额度,当额度不足时进行降级处理。
(5)流量计数器模式
- **概述:**流量计数器模式是最直观的限流方法,通过设置一个计数器,根据当前时刻的流量计数结果是否超过阈值来决定是否限流。例如,如果系统计算得出的最大持续流量是 80 TPS,那么可以控制任何一秒内,发现超过 80 次的业务请求就直接拒绝掉超额部分。
- 优点:
- **简单直观:**实现简单,容易理解和实现。
- **快速响应:**能够快速检测到流量超过阈值的情况并立即采取行动。
- **缺点:**仅针对时间点进行离散的统计
- 问题分析
- 瞬时高峰流量未被检测到
- **场景:**系统连续两秒都收到了 60 TPS 的访问请求,但这 60 TPS 请求分别是前 1 秒里的后 0.5 秒,以及后 1 秒中的前 0.5 秒所发生的。
- **结果:**虽然每个周期的流量都不超过 80 TPS 请求的阈值,但实际上系统在 1 秒内发生了超过阈值的 120 TPS 请求。
- **影响:**系统可能在某个瞬间承受了远超阈值的流量压力,导致性能问题或失败。
- 流量压力评估不准确
- **场景:**10 秒的时间片段中,前 3 秒的 TPS 平均值达到了 100,而后 7 秒的平均值是 30 左右。
- **结果:**即使连续若干秒的统计流量都超过了 80 TPS,系统仍能够处理完这些请求而不产生超时失败。
- **原因:**条件中给出的超时时间是 10 秒,而最慢的请求也能在 8 秒左右处理完毕。
- **影响:**基于固定时间周期来控制请求阈值为 80 TPS,反而会误杀一部分请求,造成部分请求出现原本不必要的失败。
- 瞬时高峰流量未被检测到
- **解决方案:**为了弥补流量计数器模式的缺陷,滑动时间窗模式被设计出来,它可以实现平滑的基于时间片段的统计。
(6)滑动窗口模式
- **概述:**动窗口算法在计算机科学的多个领域中都有广泛的应用,特别是在编译原理、网络协议和分布式系统中。该算法通过一个固定大小的窗口在时间轴上平滑移动,实时统计窗口内的数据,从而更准确地反映系统在任意时间片段内的状态。
- 应用领域:
- **编译原理:**窥孔优化(Peephole Optimization)
- **网络协议:**TCP 协议的阻塞控制(Congestion Control)
- 分布式系统:
- **服务容错:**对服务响应结果的统计
- **流量控制:**对服务请求数量的统计
- 运作过程:
- 时间轴与窗口:
- **时间轴:**表示时间的连续流动。
- **窗口:**一个固定大小的窗口,漂浮在时间轴上,与时间一起平滑地向前滚动。
- 窗口内的信息:
- **静态观察:**在任何时刻,通过窗口内观察到的信息,都等价于一段长度与窗口大小相等、动态流动中的时间片段的信息。
- **动态更新:**窗口内的数据随着窗口的移动不断更新。
- 具体实现:
- **数据结构:**通常使用双头队列(Deque)来实现。
- 工作步骤:
- **滑动窗口:**每秒将数组最后一位的元素丢弃,所有元素后移一位,然后在数组第一个位置插入一个新的空元素。
- **统计与复位:**对数组中所有元素进行统计,并复位清空计数器数据供下一个统计周期使用。
- 时间轴与窗口:
- **举例说明:**假设我们准备观察的时间片段为 10 秒,并以 1 秒作为统计精度,可以设定一个长度为 10 的数组(实际设计中通常是以双头队列来实现的)和一个每秒触发 1 次的定时器。
- 初始化:
- 数组长度为 10,初始状态为空。
- 每秒操作:
- **滑动窗口:**将数组最后一位的元素丢弃,所有元素后移一位,然后在数组第一个位置插入一个新的空元素。
- **统计:**对数组中所有元素进行统计,计算当前窗口内的总请求次数或其他统计指标。
- **复位:**清空计数器数据,准备下一个统计周期。
- 具体场景:
- **限流:**设定限流阈值为最近 10 秒内收到的外部请求不超过 500 个。当统计结果超过阈值时,拒绝超额请求。
- **容错:**设定服务熔断阈值为最近 10 秒内的故障率不超过 50%。当统计结果超过阈值时,触发熔断机制。
- 初始化:
(7)流量整形
- **定义:**流量整形是指限制网络设备的流量突变,使得网络报文以比较均匀的速度向外发送。
- **实现方式:**通常需要使用缓冲区来实现。当报文的发送速度过快时,首先在缓冲区中暂存,然后在控制算法的调节下,均匀地发送这些被缓冲的报文。
- 常用算法:
- 漏桶算法(Leaky Bucket Algorithm)
- 令牌桶算法(Token Bucket Algorithm)
(8)漏桶模式
- 概念:
- **比喻:**可以理解为一个水池,每秒以 X 升速度注水,同时又以 Y 升速度出水,问水池啥时候装满。
- **应用:**在限流模式中,可以把"请求"想象成"水",水来了都先放进池子里,水池同时又以额定的速度出水,让请求进入系统中。
- 功能:
- **缓冲区:**当请求速度过快时,水池充当缓冲区,防止出水口速度过快。
- **超时:**由于请求有超时时间,缓冲区的大小必须有限度。当注水速度持续超过出水速度一段时间后,水池会被灌满,部分请求会失败或降级。
- 实现:
- **数据结构:**漏桶是一个先入先出队列(FIFO Queue),队列长度相当于漏桶的大小。
- **拒绝机制:**当队列已满时,拒绝新的请求进入。
- 参数确定:
- 桶的大小:
- **过大:**服务可能遭遇流量过大的冲击,不能完全发挥限流作用。
- **过小:**可能误杀掉一部分正常请求。
- 流出速率:
- **固定值:**适用于固定拓扑结构的服务。
- **局限性:**现实世界的系统处理速度会受到内部拓扑结构变化和动态伸缩的影响。
- 桶的大小:
(9)令牌桶模式
- 概念:
- **比喻:**类似于银行门口的排队取号机。系统以固定速率向桶中放入令牌,请求到来时需要从桶中取出一个令牌才能进入系统。
- 功能:
- **缓冲区:**桶中最多可以存放 N 个令牌,预存的令牌是请求最大缓冲的余量。
- **超时:**当桶已空时,请求会失败或进入降级逻辑。
- 实现步骤:
- **注入令牌:**系统以限流目标决定的速率向桶中注入令牌。
- **容量限制:**桶中最多存放 N 个令牌,超出部分被丢弃。
- **请求处理:**请求到来时从桶中取走一个令牌,若桶已空则进入降级逻辑。
- 优势:
- **支持变动速率:**能够适应系统处理速度的变化,更适合现实世界的复杂场景。