MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

如何合理设置微服务熔断的触发条件

2022-09-071.5k 阅读

微服务架构中熔断机制的重要性

在微服务架构盛行的当下,系统由众多相互独立的微服务组成,这些微服务通过网络彼此通信协作,共同完成复杂的业务功能。然而,由于网络的不确定性、服务本身的故障等因素,微服务之间的调用可能会出现延迟甚至失败的情况。如果一个微服务调用失败的情况持续存在,而其他微服务仍然不断地尝试调用它,这不仅会消耗调用方的资源,如线程、连接等,还可能导致整个系统的性能下降,甚至引发级联故障,最终使整个系统瘫痪。

熔断机制就是为了解决这类问题而引入的。它类似于电路中的保险丝,当某个微服务的调用出现异常的频率达到一定程度时,熔断机制就会启动,暂时切断对该故障微服务的调用,避免调用方资源的无效消耗,同时给故障微服务一个自我修复的时间窗口。当故障微服务恢复正常后,熔断机制会逐步恢复对它的调用,以确保系统能够稳定运行。

影响熔断触发条件设置的因素

业务性质

不同业务性质的微服务对熔断触发条件的要求差异较大。例如,对于金融交易类的微服务,由于涉及资金安全等关键业务,对服务的可靠性要求极高,任何微小的故障都可能带来严重的后果。因此,这类微服务的熔断触发条件应设置得较为敏感,一旦出现少量的调用失败或延迟,就应考虑触发熔断。

而对于一些非核心的辅助性微服务,如用于获取天气信息展示在页面上的微服务,即使偶尔出现调用失败,对核心业务流程的影响也较小。在这种情况下,熔断触发条件可以设置得相对宽松,允许一定数量的失败调用,以避免频繁触发熔断对系统正常运行造成不必要的干扰。

服务间依赖关系

微服务之间存在复杂的依赖关系,一个微服务可能依赖多个其他微服务,同时也可能被多个微服务所依赖。在设置熔断触发条件时,需要考虑该微服务在整个依赖链中的位置和重要性。

如果一个微服务处于依赖链的底层,被多个上层微服务广泛依赖,那么它的故障可能会迅速波及到众多其他微服务。对于这样的关键底层微服务,应设置较为严格的熔断触发条件,以便在其出现问题时能够尽快切断调用,防止故障扩散。

相反,如果一个微服务只是依赖其他微服务,但很少被其他微服务依赖,那么它的熔断触发条件可以相对宽松一些。因为即使它出现故障,对整个系统的影响范围相对较小。

系统负载与资源状况

系统的负载和资源状况也是影响熔断触发条件设置的重要因素。当系统处于高负载状态时,资源(如CPU、内存、网络带宽等)紧张,此时微服务的调用可能会因为资源不足而出现更多的延迟和失败情况。

在这种情况下,需要综合考虑故障是由于资源不足导致的暂时现象,还是微服务本身的真正故障。如果是资源问题导致的,可能需要适当调整熔断触发条件,避免在资源紧张时频繁触发熔断。例如,可以适当增加失败调用的阈值或者延长统计失败调用的时间窗口,给微服务在资源恢复后正常运行的机会。

而当系统资源充足时,微服务的调用失败更有可能是服务本身的问题,此时熔断触发条件可以设置得更为严格,以便及时发现并处理故障。

熔断触发条件的具体设置维度

失败调用次数

失败调用次数是最直观的熔断触发条件之一。可以设定在一个特定的时间窗口内,当对某个微服务的失败调用次数达到一定阈值时,触发熔断。例如,在1分钟内,如果对某个微服务的调用失败次数超过50次,就触发熔断。

下面以Java语言中使用Hystrix框架为例,展示如何通过代码设置基于失败调用次数的熔断触发条件:

HystrixCommandProperties.Setter properties = HystrixCommandProperties.Setter()
  .withCircuitBreakerRequestVolumeThreshold(20) // 在统计时间窗口内,至少有20次调用才会开启熔断检查
  .withCircuitBreakerErrorThresholdPercentage(50); // 失败率达到50%时,触发熔断

在上述代码中,withCircuitBreakerRequestVolumeThreshold设置了统计时间窗口内的最小调用次数,只有当调用次数达到这个值时,才会根据失败率来判断是否触发熔断。withCircuitBreakerErrorThresholdPercentage则设置了失败率阈值,当失败率超过这个百分比时,熔断机制启动。

失败率

失败率是另一个重要的熔断触发维度。通过计算失败调用次数与总调用次数的比例来确定失败率。例如,在一段时间内,对某个微服务进行了100次调用,其中有30次失败,那么失败率就是30%。当失败率超过预先设定的阈值时,触发熔断。

使用Hystrix框架设置基于失败率的熔断触发条件代码如下:

HystrixCommandProperties.Setter properties = HystrixCommandProperties.Setter()
  .withCircuitBreakerRequestVolumeThreshold(10)
  .withCircuitBreakerErrorThresholdPercentage(30);

这里设置了在统计时间窗口内至少有10次调用,且失败率达到30%时触发熔断。相比仅依赖失败调用次数,失败率能更全面地反映微服务的健康状况,因为它考虑了总调用次数的因素。如果总调用次数很少,即使失败次数不多,也可能意味着服务存在较大问题;而如果总调用次数很多,少量的失败可能在可接受范围内。

调用延迟

除了失败调用,调用延迟也是判断微服务是否健康的重要指标。在一些对响应时间要求较高的业务场景中,即使微服务没有出现调用失败,但如果调用延迟过长,也会影响整个系统的性能和用户体验。

可以设定一个延迟阈值,当对某个微服务的调用平均延迟超过这个阈值时,触发熔断。例如,规定对某个微服务的调用平均延迟不能超过100毫秒,如果连续多次调用的平均延迟超过这个值,就触发熔断。

以Spring Cloud Alibaba Sentinel框架为例,设置基于调用延迟的熔断触发条件代码如下:

SentinelResourceDefinition resourceDefinition = new SentinelResourceDefinition();
resourceDefinition.setName("myService");
// 设置RT(响应时间)熔断规则,平均响应时间超过200毫秒触发熔断
RtRule rtRule = new RtRule();
rtRule.setResource("myService");
rtRule.setCount(200);
rtRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rtRule.setTimeWindow(10);
List<Rule> rules = new ArrayList<>();
rules.add(rtRule);
resourceDefinition.setRules(rules);

在上述代码中,RtRule用于定义基于响应时间的熔断规则,setCount设置了平均响应时间的阈值为200毫秒,setTimeWindow设置了熔断后的熔断时长为10秒。

异常类型

不同类型的异常对微服务的影响程度不同,有些异常可能只是临时性的问题,而有些异常则可能意味着服务出现了严重故障。因此,可以根据异常类型来设置熔断触发条件。

例如,对于一些常见的网络超时异常,可以允许一定数量的出现,因为网络问题可能是短暂的。但对于一些业务逻辑异常(如数据库约束违反、关键业务逻辑错误等),则应更加敏感,一旦出现这类异常,就触发熔断。

在Java代码中,可以通过自定义Hystrix的异常处理逻辑来实现根据异常类型触发熔断:

public class CustomHystrixCommand extends HystrixCommand<String> {
    public CustomHystrixCommand(Setter setter) {
        super(setter);
    }

    @Override
    protected String run() throws Exception {
        // 实际的微服务调用逻辑
        throw new BusinessLogicException("关键业务逻辑错误");
    }

    @Override
    protected String getFallback() {
        // 熔断后的降级处理逻辑
        return "fallback value";
    }

    @Override
    protected boolean isFailureAllowed(Throwable e) {
        if (e instanceof BusinessLogicException) {
            return true; // 业务逻辑异常允许触发熔断
        }
        return false;
    }
}

在上述代码中,isFailureAllowed方法用于判断异常类型是否允许触发熔断,这里对BusinessLogicException类型的异常允许触发熔断。

综合设置熔断触发条件的策略

多维度组合

在实际应用中,单一维度的熔断触发条件往往不足以准确判断微服务的健康状况,需要综合考虑多个维度。例如,可以同时设置失败调用次数、失败率和调用延迟三个维度的触发条件。

当失败调用次数超过一定阈值,且失败率达到一定比例,同时调用延迟也超过阈值时,才触发熔断。这样可以更全面地评估微服务的运行状态,避免因单一维度的误判而频繁触发或错过熔断时机。

以Hystrix框架为例,综合设置多维度熔断触发条件代码如下:

HystrixCommandProperties.Setter properties = HystrixCommandProperties.Setter()
  .withCircuitBreakerRequestVolumeThreshold(30)
  .withCircuitBreakerErrorThresholdPercentage(40)
  .withExecutionTimeoutInMilliseconds(150);

在上述代码中,设置了在统计时间窗口内至少有30次调用,失败率达到40%,且调用执行超时时间为150毫秒(即调用延迟超过150毫秒视为失败)时,触发熔断。

动态调整

由于微服务系统的运行环境是动态变化的,业务流量、系统负载等因素都可能随时发生改变,因此熔断触发条件也不应是固定不变的,而应该具备动态调整的能力。

可以通过监控系统实时收集微服务的调用数据,包括失败次数、失败率、调用延迟等指标。根据这些实时数据,结合机器学习算法或预设的策略,动态地调整熔断触发条件。

例如,当业务流量大幅增加时,系统负载升高,此时可以适当放宽熔断触发条件,如增加失败调用次数阈值或失败率阈值,以避免因资源紧张导致的误熔断。而当业务流量平稳且系统资源充足时,再收紧熔断触发条件,及时发现并处理微服务的故障。

以下是一个简单的基于动态调整失败率阈值的示例代码(使用Python和Flask框架模拟监控系统与熔断条件调整):

from flask import Flask, jsonify
import time

app = Flask(__name__)

# 模拟实时监控数据
failure_count = 0
total_count = 0
failure_rate_threshold = 30  # 初始失败率阈值

@app.route('/monitor', methods=['POST'])
def monitor():
    global failure_count, total_count, failure_rate_threshold
    data = request.get_json()
    if data['status'] == 'failure':
        failure_count += 1
    total_count += 1
    current_failure_rate = (failure_count / total_count) * 100 if total_count > 0 else 0

    # 根据业务流量动态调整失败率阈值
    if current_failure_rate < 20 and total_count > 100:
        failure_rate_threshold = 25
    elif current_failure_rate > 40 and total_count > 100:
        failure_rate_threshold = 35

    return jsonify({'current_failure_rate': current_failure_rate, 'failure_rate_threshold': failure_rate_threshold})

if __name__ == '__main__':
    app.run(debug=True)

在上述代码中,通过/monitor接口接收微服务调用的状态数据,实时计算失败率,并根据当前失败率和总调用次数动态调整失败率阈值。

灰度发布与测试

在设置熔断触发条件时,为了确保不会对线上业务造成过大影响,应充分利用灰度发布和测试机制。

在灰度发布阶段,可以先在一小部分用户或流量上应用新设置的熔断触发条件,密切观察系统的运行状况。如果发现熔断机制出现误判或未能及时触发熔断等问题,可以及时调整触发条件,然后再逐步扩大灰度范围,直到完全应用到所有用户和流量。

同时,在测试环境中,应模拟各种可能的故障场景,对不同的熔断触发条件进行全面测试。通过在测试环境中不断优化熔断触发条件,使其在实际生产环境中能够更加准确、稳定地发挥作用。

例如,在测试环境中模拟微服务的网络延迟、调用失败等故障场景,测试不同的失败调用次数阈值、失败率阈值和调用延迟阈值下,熔断机制是否能够及时、准确地触发,以及熔断后的降级处理是否符合业务预期。

总结熔断触发条件设置的注意事项

避免过度敏感与迟钝

在设置熔断触发条件时,要在敏感与迟钝之间找到平衡。如果触发条件设置得过于敏感,微服务稍有波动就触发熔断,会导致不必要的服务中断,影响业务的正常运行。例如,在网络抖动等短暂性问题时频繁触发熔断,使得服务无法及时恢复正常调用。

反之,如果触发条件设置得过于迟钝,微服务已经出现严重故障,却未能及时触发熔断,会导致故障持续蔓延,消耗更多系统资源,甚至引发整个系统的崩溃。因此,需要根据业务实际情况和微服务的特性,仔细权衡并不断优化触发条件。

考虑系统的自愈能力

有些微服务在出现故障后,具有一定的自愈能力。例如,由于网络短暂中断导致的调用失败,当网络恢复后,微服务可能会自动恢复正常。在设置熔断触发条件时,要充分考虑微服务的这种自愈能力。

可以适当设置一个较短的熔断时长,给微服务一个自我恢复的机会。同时,在熔断恢复后,逐步增加对微服务的调用量,观察其是否真正恢复正常,而不是立即恢复到正常的调用频率。这样可以避免在微服务尚未完全恢复时,大量的调用再次导致其故障。

监控与预警

无论熔断触发条件设置得多么合理,都需要建立完善的监控与预警机制。通过监控系统实时跟踪微服务的调用指标,如失败次数、失败率、调用延迟等,一旦发现这些指标接近或超出熔断触发条件,及时发出预警。

运维人员可以根据预警信息,提前介入并分析微服务的运行状况,判断是否需要调整熔断触发条件,或者对微服务本身进行优化和修复。同时,监控数据也可以为后续熔断触发条件的优化提供依据,不断完善熔断机制在系统中的应用。

综上所述,合理设置微服务熔断的触发条件是保障微服务架构系统稳定运行的关键环节。需要综合考虑业务性质、服务间依赖关系、系统负载等多种因素,从失败调用次数、失败率、调用延迟、异常类型等多个维度进行设置,并采用多维度组合、动态调整等策略,同时结合灰度发布、测试以及监控预警等手段,确保熔断机制能够准确、及时地发挥作用,避免系统出现级联故障,提高整个微服务架构系统的可靠性和可用性。