前言
通过前文我们知道,当下游服务偶尔出现超时的状况时,借助超时重试机制可以提高服务的可用性。然而,一旦下游服务出现容量过载这类较为严重的问题时,重试便不再是提升服务可用性的有效手段了。在这种情况下,我们必须通过其它方法,来保障服务的稳定性。
下游服务过载的影响
一旦下游服务过载,如果我们还在源源不断地请求下游服务,那么新的请求就会不断地在下游堆积,排队等待处理。这些积压的请求会大量占用连接、内存等资源,让下游服务很难恢复。
此外,由于我们的服务需要长时间等待下游服务的响应,这将导致我们服务大量的协程和连接资源被占用。
在极端情况下,面对下游服务过载,持续不断的请求导致下游迟迟无法恢复,甚至可能拖垮我们的整个系统,进而引发雪崩效应。
熔断机制
在电路系统里,当电流超过安全阈值时,保险丝会自动熔断,切断电路,从而避免电器设备因电流过载而受损。在服务治理领域,同样存在与之类似的熔断机制来应对服务过载问题。
熔断机制指的是当调用某个服务出现超时等指定的错误,并且在一段时间内的错误率超过了预先设定的阈值时,我们的服务就自动停止向下游服务发送后续请求,转而暂时返回错误信息或者备用数据。这一机制能有效防止因单个服务故障引发的连锁反应,避免故障范围继续扩大。
熔断器的核心在于为每个调用对象维护下面三种状态,并在这三个状态之间转换
- 闭合状态(closed):处于这种状态时,每次对下游服务发起调用,系统都会记录指定错误的失败次数以及总调用次数。在一定的时间窗口内,一旦失败次数达到预设阈值,就会触发熔断,状态转变为断开状态。
- 断开状态(open):在该状态下,针对下游的请求将不再调用后端服务,而是直接立即返回错误响应。与此同时,会启动一个计时器,当定时器计时结束,便会进入半熔断状态。
- 半打开/半熔断状态(half-open):在这一状态下,系统允许向故障服务发送少量试探性请求,这是为了检测服务是否已恢复正常。如果这些试探性调用都能正常完成,就可判定被调用服务已恢复,这时熔断器会切回闭合状态,同时重置相关计数。如果这些试探性调用中仍存在失败的情况,那就表明被调用服务尚未恢复,熔断器会重新切换到断开状态,并重置计时器。半熔断状态能有效避免正在恢复的服务,因突然大量涌入的请求而再次被打垮。
熔断粒度选择
- 服务粒度熔断:如果调用某个下游服务的失败或超时次数达到设定的阈值,就触发熔断。不过这种方式过于 “粗暴”,如果下游服务只是某个接口响应慢,会导致对这个服务的其它接口调用也全部被熔断。
- 接口粒度熔断:针对下游的每个接口独立统计调用失败和超时次数,当某个接口达到熔断阈值时,仅对下游的这个接口进行熔断,不影响其它接口的正常调用。这种粒度更精细,能在下游部分接口出现问题时,尽量不影响其它接口的调用。
- 实例粒度熔断:当下游某个实例出现故障,导致调用这个实例的失败或超时次数达到阈值时,仅熔断对该实例的调用,其他正常实例仍可被调用,主要用于解决单实例异常问题。
在实际应用场景里,由于经常会出现因调用特定接口致使下游过载,或者由于容器混部引发单实例过载的现象,为了更精准地应对这些常见的过载问题,我们会同时启用接口粒度和实例粒度的熔断。
如何判断下游过载?
熔断机制的主要作用是缓解系统过载,因此我们需要准确识别与系统过载相关的错误类型,也就是判断什么样的错误类型需要进行熔断计数,以便进行有效的熔断处理。
常见的过载相关错误包括超时和限流等,而应用层的错误,比如“参数校验不通过”,则不在熔断机制的处理范围内。
当然,在实际应用中,微服务框架一般都会集成熔断器,我们无需自行开发。如果我们使用的微服务框架没有集成熔断器,也有不少开源的第三方组件可供选择。例如阿里开源的 sentinel-golang
降级机制
虽然熔断机制能够有效隔离出现故障的服务,防止级联故障的发生,避免故障范围进一步扩大。但是当下游服务在短时间内无法恢复正常运行时,仅仅依靠熔断机制是不够的。这时我们需要从业务功能层面入手,搭配降级的方案来实现快速止损。
降级是指服务本身资源不足或下游服务不可用时,通过提供默认结果或关闭非核心功能等手段,来保证系统的核心功能能够继续运行,从而提高系统的可用性和稳定性。
降级方案的核心在于精心设计降级策略。由于业务场景的多样性,相应的降级策略也会各不相同。常见策略有
- 返回兜底数据:当从下游服务获取数据失败时,返回预先准备好的备用数据,维持业务基本展示或操作。兜底数据可以是静态数据,也可以是缓存中的旧数据。例如,对于电商个性化推荐功能,当调用下游服务失败,无法实现个性化推荐时,我们可以直接用热门商品作为兜底返回,推荐给用户。
- 异步处理:为了避免因等待操作完成而阻塞系统资源,我们可以将原本的同步操作转换为异步操作。特别是在写数据场景中,将同步写转换为异步写,可以提高系统的响应速度和吞吐量。例如,在进行秒杀活动时,我们可以将下单请求的处理降级为异步模式。先把下单信息发送到消息队列暂存,随后由异步处理程序从队列中获取下单消息进行处理,最后将订单信息持久化写入数据库。这样一来,由于异步处理程序的消费速度是可控的,我们就能有效调控写入数据库的 TPS(Transactions Per Second),防止数据库压力过大,从而保障系统的稳定性。
- 关闭非核心功能:当系统面临压力时,我们可以暂时关停非核心功能,将资源集中于核心业务,确保关键业务流程的顺畅运行。例如,某些服务可能具有用户访问记录的上报功能,这些上报的数据主要用于数据分析团队进行离线分析和异常问题排查。在流量较大时,我们可以将这些功能暂时关闭,减少系统资源的消耗,从而降低服务的负载。
关于降级方式的选择,我们有两种主要途径:
- 利用远程配置开关来实现灵活的降级控制,远程配置开关具有较强的可控性,但它的缺点在于需要人工操作,在系统遭遇突发状况时,可能因反应不够迅速而使系统压力增大。
- 依赖自动降级机制以实现快速响应。无需人工干预,它能够依据预先设定的规则(例如,当调用下游服务出错时)自动启动,从而迅速应对各种异常情况。不过这个方式的缺点是可控性相对较低,一旦启动,人工不好干预。
为了防止降级功能因长期没有使用而失效,我们还需要定期演练,确保降级的有效性。例如各大电商平台在每次大促前都会有压测,只有这样,才能确保在真正面临系统危机时,降级措施切实发挥作用,有力保障系统的稳定性与可用性。
评论记录:
回复评论: