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

对比剖析不同微服务架构下的熔断策略

2022-12-195.2k 阅读

微服务架构概述

在当今的软件开发领域,微服务架构已然成为构建大型分布式系统的主流选择。它将一个庞大的单体应用拆分成多个小型、独立且自治的服务,每个服务专注于单一的业务功能。这种架构风格带来了诸多优势,如易于开发、部署和维护,可独立扩展等。然而,随着微服务数量的增多和系统复杂度的提升,服务之间的依赖关系变得错综复杂,一个服务的故障可能会级联影响到其他服务,进而导致整个系统的崩溃。为了应对这一挑战,熔断策略应运而生。

常见微服务架构模式

单体微服务架构

在单体微服务架构中,虽然整体应用被拆分成多个服务,但这些服务可能仍然共享一些基础设施,如数据库连接池、消息队列等。各个微服务之间通过本地接口或者轻量级的通信协议进行交互。这种架构模式相对简单,开发和部署成本相对较低。但由于服务之间存在紧密的依赖关系,一旦某个核心服务出现故障,很容易引发连锁反应。例如,假设一个电商系统中,订单服务依赖于库存服务。如果库存服务因为某种原因(如数据库连接超时)出现故障,订单服务在持续调用库存服务的过程中,会占用大量的资源,如线程池中的线程,最终导致订单服务自身也无法处理其他请求,进而影响到整个电商系统的订单处理流程。

分布式微服务架构

分布式微服务架构则更强调服务的独立性和分布式特性。每个微服务都有自己独立的运行环境,包括独立的数据库、消息队列等。服务之间通过网络进行通信,常见的通信协议有 HTTP/REST 或者 gRPC 等。这种架构模式虽然提高了系统的可扩展性和容错性,但也增加了系统的复杂度。由于服务分布在不同的节点上,网络延迟、网络故障等问题更容易出现。比如,一个跨国公司的全球业务系统,各个地区的微服务可能部署在不同的数据中心。当某个地区的数据中心网络出现波动时,其他地区的微服务在调用该地区服务时就可能出现连接超时等问题,这同样需要有效的熔断策略来防止故障的扩散。

熔断策略的基本概念

熔断策略源自于电路中的保险丝原理。当电路中的电流过大时,保险丝会熔断,从而切断电路,保护电器设备。在微服务架构中,熔断策略用于监控服务调用的健康状况。当对某个服务的调用失败次数或者失败率达到一定阈值时,熔断机制就会启动,暂时切断对该服务的调用,避免资源的无效消耗。熔断状态通常有三种:关闭(Closed)、打开(Open)和半打开(Half - Open)。

关闭状态

在正常情况下,熔断处于关闭状态。此时,对目标服务的调用正常进行,熔断器会统计调用的成功和失败次数等指标。例如,一个用户服务调用商品服务获取商品详情,每次调用都能成功返回数据,熔断器记录的成功次数不断增加,失败次数为零,这种情况下熔断就处于关闭状态。

打开状态

当调用失败次数或者失败率超过设定的阈值时,熔断进入打开状态。在打开状态下,对该服务的所有调用都会立即返回一个预设的错误响应,而不再真正调用目标服务。这就像是电路中的保险丝熔断后,电流无法通过。比如,假设商品服务因为数据库故障,连续多次调用失败,失败率超过了 80%(阈值),此时熔断器打开,用户服务再次调用商品服务时,不会等待商品服务的响应,而是直接得到一个熔断后的错误提示,告知用户商品服务暂时不可用。

半打开状态

为了让系统有机会恢复,熔断器在打开一段时间后会进入半打开状态。在半打开状态下,熔断器会允许少量的调用尝试去访问目标服务。如果这些少量的调用成功,说明目标服务可能已经恢复,熔断器就会切换回关闭状态;如果仍然失败,熔断器会再次回到打开状态。例如,商品服务在故障修复后,熔断器进入半打开状态,允许 10 次调用尝试。如果这 10 次中有 8 次成功,那么熔断器就会认为商品服务已恢复正常,切换回关闭状态,后续调用可以正常进行。

不同微服务架构下的熔断策略对比

单体微服务架构下的熔断策略

  1. 实现方式:在单体微服务架构中,由于服务之间的通信相对简单,熔断策略的实现可以基于本地的一些技术框架。例如,使用 Hystrix 框架。Hystrix 可以通过注解的方式轻松地在代码中实现熔断功能。假设在订单服务中调用库存服务的代码如下:
@Service
public class OrderService {
    @Autowired
    private InventoryService inventoryService;

    @HystrixCommand(fallbackMethod = "fallbackCheckInventory")
    public boolean checkInventory(int productId, int quantity) {
        return inventoryService.checkInventory(productId, quantity);
    }

    public boolean fallbackCheckInventory(int productId, int quantity) {
        // 熔断后的降级处理逻辑,比如记录日志,返回默认值等
        log.error("Inventory service is unavailable.");
        return false;
    }
}

在上述代码中,@HystrixCommand 注解标记了 checkInventory 方法,当对 inventoryService 的调用出现故障时,会自动调用 fallbackCheckInventory 方法进行降级处理。

  1. 特点:这种方式的优点是实现简单,对现有代码的侵入性相对较小。开发人员只需要在调用其他服务的方法上添加相应的注解即可实现熔断功能。而且,由于服务共享一些基础设施,在熔断过程中对资源的控制相对容易。例如,在单体应用中,如果某个服务熔断,开发人员可以更容易地调整数据库连接池等资源,以应对服务故障带来的影响。然而,缺点也很明显,由于服务之间存在紧密的依赖关系,一旦某个服务熔断,可能会对整个应用的业务流程产生较大影响。而且,单体应用的升级和维护可能会因为熔断策略的存在变得更加复杂,因为所有的熔断逻辑都集中在单体应用内部。

分布式微服务架构下的熔断策略

  1. 实现方式:在分布式微服务架构中,由于服务分布在不同的节点上,需要采用更分布式的熔断解决方案。例如,使用阿里的 Sentinel 框架。Sentinel 可以通过配置中心来统一管理各个微服务的熔断规则。以一个使用 Spring Cloud Alibaba Sentinel 的示例来说,首先在 pom.xml 文件中添加 Sentinel 依赖:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

然后在 application.yml 文件中配置 Sentinel 相关参数:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

接着,可以通过 Sentinel 的控制台来配置熔断规则。例如,对于一个名为 product - service 的服务,设置其熔断规则为:当平均响应时间超过 200ms,并且在 10 秒内请求数达到 50 次时,触发熔断,熔断时间为 5 秒。 2. 特点:分布式熔断策略的优点在于其灵活性和可扩展性。通过配置中心可以方便地对不同的微服务设置不同的熔断规则,适应复杂多变的业务场景。而且,由于各个微服务相对独立,一个服务的熔断对其他服务的影响相对较小。例如,在一个大型电商系统中,商品服务熔断后,用户服务仍然可以正常处理用户登录等其他业务。然而,这种方式的缺点是配置和管理相对复杂。需要专门的配置中心和控制台来管理熔断规则,对运维人员的技术要求较高。而且,由于服务之间通过网络通信,网络延迟等因素可能会影响熔断策略的准确性。比如,由于网络抖动导致的短暂延迟,可能会被误判为服务故障从而触发熔断。

熔断策略的关键参数与优化

关键参数

  1. 失败阈值:这是决定熔断是否打开的重要参数。失败阈值可以通过失败次数或者失败率来衡量。例如,在 Hystrix 中,可以设置在一定时间窗口内(如 10 秒),如果失败次数超过 20 次,则触发熔断。在设置失败阈值时,需要综合考虑服务的正常波动情况。如果阈值设置过低,可能会因为一些短暂的异常而频繁触发熔断,影响系统的正常运行;如果阈值设置过高,又可能无法及时发现真正的服务故障。比如,一个查询类的服务,在高并发情况下可能会出现偶尔的超时,但这并不一定意味着服务出现了严重故障。如果失败阈值设置得过低,就可能导致正常的高并发请求场景下也频繁触发熔断。
  2. 熔断时间:即熔断打开后保持的时间。熔断时间过短,可能服务还未真正恢复就又开始调用,导致再次熔断;熔断时间过长,则会使服务长时间不可用,影响业务。例如,对于一个依赖外部第三方接口的服务,由于第三方接口可能会出现短暂的维护升级,此时设置 1 - 2 分钟的熔断时间比较合适,既能保证在第三方接口维护期间不会无效调用,又能在其恢复后及时恢复服务调用。
  3. 半打开尝试次数:在半打开状态下,允许尝试调用目标服务的次数。如果这个次数设置得太少,可能无法准确判断服务是否真的恢复;如果设置得太多,又可能在服务未完全恢复时消耗过多资源。比如,对于一个相对稳定的服务,可以设置较少的尝试次数,如 5 次;而对于一个经常出现波动的服务,可能需要设置 10 - 15 次尝试次数。

优化策略

  1. 动态调整参数:可以根据服务的运行状态和业务需求动态调整熔断的关键参数。例如,在业务高峰期,可以适当提高失败阈值,避免因为短暂的高并发压力导致的误熔断;在业务低谷期,可以降低阈值,及时发现潜在的服务问题。可以通过监控系统收集服务的性能指标,如调用成功率、响应时间等,然后利用机器学习算法或者简单的规则引擎来动态调整参数。比如,通过实时监控服务的调用成功率,如果发现成功率在一段时间内持续稳定在较高水平,可以适当降低失败阈值,以提高对故障的敏感度。
  2. 结合多种指标:除了单纯的失败次数和失败率,还可以结合其他指标来判断是否触发熔断。例如,响应时间也是一个重要的指标。如果服务的响应时间突然大幅增加,即使失败次数未达到阈值,也可能意味着服务出现了性能问题,此时可以考虑触发熔断。还可以结合服务的资源利用率,如 CPU 使用率、内存使用率等。如果某个服务的 CPU 使用率持续超过 80%,并且响应时间变长,即使失败次数不多,也可能需要熔断,以避免服务进一步恶化影响其他服务。

熔断策略与其他容错机制的结合

与降级策略结合

降级策略是在服务出现故障或者资源紧张时,采取的一种牺牲部分功能来保证核心功能可用的策略。熔断和降级通常紧密结合。当熔断打开时,就需要执行降级逻辑。例如,在一个新闻资讯应用中,当调用图片服务获取新闻配图出现熔断时,可以采用降级策略,将新闻中的图片部分替换为文字描述,保证用户仍然可以获取新闻的核心内容。在代码实现上,可以在熔断的 fallback 方法中实现降级逻辑。如:

@HystrixCommand(fallbackMethod = "fallbackGetImage")
public String getImage(String newsId) {
    return imageService.getImage(newsId);
}

public String fallbackGetImage(String newsId) {
    // 降级逻辑,返回文字描述
    return "This news has no available image.";
}

通过这种结合方式,可以在服务故障时尽可能减少对用户体验的影响。

与重试策略结合

重试策略是指在调用服务失败后,尝试重新调用。它与熔断策略可以相互补充。在熔断关闭状态下,如果调用失败,可以先进行一定次数的重试。例如,对于一些因为网络抖动等瞬时故障导致的调用失败,重试可能会成功。但如果重试多次仍然失败,并且达到了熔断的阈值,就触发熔断。在 Hystrix 中,可以通过配置 maxAttempts 参数来设置重试次数。如:

@HystrixCommand(fallbackMethod = "fallbackGetData", commandProperties = {
    @HystrixProperty(name = "execution.isolation.thread.maxAttempts", value = "3")
})
public String getData(String param) {
    return remoteService.getData(param);
}

public String fallbackGetData(String param) {
    // 熔断后的降级处理
    return "Data retrieval failed.";
}

这样,在调用 remoteService 失败时,会尝试最多 3 次重试,如果仍然失败,则触发熔断并执行降级逻辑。

不同行业场景下的熔断策略应用差异

金融行业

在金融行业,系统的稳定性和数据的准确性至关重要。例如,在银行的转账业务中,一个微服务可能负责验证账户余额,另一个微服务负责执行转账操作。如果验证余额的服务出现故障,触发熔断后,不能简单地返回错误信息。因为这可能会导致转账业务无法继续,给客户带来损失。在这种情况下,熔断策略需要与补偿机制相结合。当验证余额服务熔断时,系统可以先冻结转账操作相关的资金,并记录日志。然后通过人工介入或者后续重试来完成验证操作。而且,金融行业对数据的一致性要求很高,所以在熔断过程中,要确保数据不会出现不一致的情况。例如,在证券交易系统中,订单服务和交易撮合服务之间的调用,如果订单服务熔断,要保证已经提交的订单数据不会丢失或者出现重复交易的情况。

电商行业

电商行业的特点是流量波动大,尤其是在促销活动期间。在这种情况下,熔断策略需要具备很强的动态调整能力。例如,在“双 11”等大促活动时,商品服务可能会因为高并发而出现响应变慢甚至短暂故障。此时,熔断策略的失败阈值和熔断时间需要根据实时的流量情况进行动态调整。在流量高峰前,可以适当提高失败阈值,避免因为瞬时的高并发请求导致误熔断。同时,电商行业对用户体验的要求也很高。当某个服务熔断时,降级策略要尽可能地保证用户仍然可以进行关键操作。比如,当库存服务熔断时,商品详情页面可以显示“库存可能不准确,以提交订单时为准”等提示信息,同时允许用户继续下单,在后续流程中再处理库存问题,以保证用户的购物流程不会被完全打断。

医疗行业

医疗行业的系统涉及到患者的生命安全和医疗数据的保密性。在医疗信息系统中,各个微服务之间的调用必须高度可靠。例如,电子病历服务和检查检验结果服务之间的调用。如果检查检验结果服务出现故障触发熔断,不能简单地返回错误。因为医生可能正在根据这些结果为患者制定治疗方案。此时,熔断策略可以采用缓存机制。当服务熔断时,先从本地缓存中获取最近一次的检查检验结果(前提是数据的时效性可以接受),提供给医生参考。同时,系统要尽快通知运维人员修复故障服务。而且,医疗行业对数据的安全性要求极高,在熔断和降级过程中,要确保患者的隐私数据不会泄露。例如,在处理患者敏感信息的微服务熔断时,降级处理不能将敏感信息以明文形式返回给前端。

总结与展望

不同微服务架构下的熔断策略各有特点,单体微服务架构下的熔断实现简单但对整体业务影响较大,分布式微服务架构下的熔断灵活可扩展但配置管理复杂。在实际应用中,需要根据业务场景、系统规模等因素选择合适的熔断策略,并结合其他容错机制进行优化。同时,随着微服务架构的不断发展,熔断策略也需要不断演进。未来,可能会出现更加智能化的熔断策略,能够根据实时的系统状态和业务需求自动调整参数,并且更好地与新兴技术如容器化、无服务器架构等相结合,为构建更加稳定、可靠的分布式系统提供有力保障。在不同行业场景下,熔断策略要充分考虑行业特点,确保系统的稳定性、可靠性和安全性。总之,熔断策略作为微服务架构中保障系统容错性的关键技术,将在未来的软件开发中继续发挥重要作用。