为什么有了服务降级,还需要服务熔断?

大家好呀,我是猿java

在分布式系统中,我们经常听到服务熔断这个词,那么,什么是服务熔断?为什么需要服务熔断?如何实现服务熔断?这篇文章,我们还是来聊一道招行2面道题目:为什么有了服务降级还需要服务熔断?

1. 什么是服务熔断?

简单来说,服务熔断(Circuit Breaker)是一种用于提高分布式系统健壮性的设计模式。它的灵感来源于电路中的熔断器,当电路中出现问题时,熔断器会自动断开,防止故障扩大,保护整个系统。应用在微服务架构中,服务熔断机制可以在某个服务出现故障或响应缓慢时,快速失败或采取备用方案,从而避免级联失败,提升系统的整体稳定性。

2. 原理分析

接下来,我们讲解服务熔断的原理,整体总结成下面 5个步骤。

2.1 正常状态

在正常情况下,服务之间的调用是通畅的,熔断器处于关闭状态。所有请求都会正常发送到目标服务,没有任何干预。

2.2 监控与检测

熔断器会监控目标服务的调用情况,包括请求成功率、失败率、响应时间等。当某个阈值被超过(比如连续失败次数超过预设值),熔断器会认为目标服务可能出现了问题。

2.3 打开熔断

一旦检测到目标服务可能故障,熔断器会打开(Open) ,此时所有对该服务的请求都会被立即失败,不再发送实际请求。这就像是电路中的熔断器断开一样,防止故障蔓延。

2.4 半开启状态

过一段时间后,熔断器会进入半开启状态(Half-Open),允许少量请求尝试调用目标服务。如果这些请求成功,熔断器会重新关闭,恢复正常状态;如果失败,熔断器继续保持打开状态。

2.5 备用机制

当熔断器打开时,可以采取备用方案,比如返回默认值、跳过某些操作,甚至切换到其他服务实例,以保证系统的部分功能仍然可用。

通过这样的机制,服务熔断能够有效地防止单个服务故障导致的系统级别的连锁反应。

3. 示例演示

为了更好地理解服务熔断,接下来,我们将使用 Resilience4j 这个轻量级的容错库来实现服务熔断机制。Resilience4j是一个专为 Java 8及以上版本设计的库,具有易用性和高性能的特点。

3.1 环境准备

首先,确保你的项目中已经引入了Resilience4j的依赖。以Maven项目为例,添加以下依赖到pom.xml中:

xml 复制代码
<dependencies>
    <!-- Resilience4j核心依赖 -->
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-all</artifactId>
        <version>2.0.2</version>
    </dependency>
    <!-- 其他依赖项 -->
</dependencies>

3.2 编写服务熔断代码

下面是一个简单的示例,展示如何使用Resilience4j实现服务熔断。当目标服务响应慢或失败时,熔断器会起作用,快速返回备用结果。

java 复制代码
import io.github.resilience4j.circuitbreaker.*;
import io.github.resilience4j.decorators.Decorators;

import java.time.Duration;
import java.util.concurrent.*;
import java.util.function.Supplier;

public class CircuitBreakerDemo {

    public static void main(String[] args) {
        // 创建CircuitBreaker配置
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(50) // 失败率阈值
                .waitDurationInOpenState(Duration.ofSeconds(5)) // 打开状态持续时间
                .slidingWindowSize(4) // 滑动窗口大小
                .build();

        // 创建CircuitBreaker实例
        CircuitBreaker circuitBreaker = CircuitBreaker.of("myCircuitBreaker", config);

        // 模拟目标服务调用
        Supplier<String> decoratedSupplier = Decorators.ofSupplier(() -> callExternalService())
                .withCircuitBreaker(circuitBreaker)
                .withFallback(Collections.singletonList(CircuitBreaker.class), 
                             throwable -> "默认响应")
                .decorate();

        // 模拟多次调用
        for (int i = 0; i < 10; i++) {
            try {
                String response = decoratedSupplier.get();
                System.out.println("响应: " + response);
            } catch (Exception e) {
                System.out.println("调用失败: " + e.getMessage());
            }

            // 等待1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    // 模拟外部服务调用,随机失败或延时
    private static String callExternalService() {
        double random = Math.random();
        if (random < 0.5) {
            // 模拟失败
            throw new RuntimeException("服务调用失败");
        } else {
            // 模拟延时
            try {
                Thread.sleep(2000); // 2秒延时
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "成功响应";
        }
    }
}

3.3 代码解析

  1. 配置CircuitBreaker:我们创建了一个自定义的熔断器配置,设置了失败率阈值为50%,滑动窗口大小为4次调用,打开状态持续5秒。

  2. 装饰目标服务调用 :使用Decorators将目标服务调用装饰为一个有熔断器保护的供应者(Supplier)。同时,我们设置了一个备用响应,当熔断器打开或目标服务调用失败时,返回"默认响应"。

  3. 模拟调用 :在for循环中,我们模拟了多次服务调用。目标服务callExternalService随机成功或失败,并可能产生延时。通过这种方式,我们可以观察熔断器是如何根据调用结果自动切换状态的。

运行这段代码,当失败率超过 50%时,熔断器会打开,后续的请求会立即返回"默认响应"。经过 5秒后,熔断器会进入半开启状态,尝试恢复调用。如果目标服务恢复正常,熔断器会重新关闭,系统恢复正常运行。

5. 问题解答

回到文章的标题:为什么有了服务降级,还需要服务熔断?

这里我们总结了4个核心理由:

  1. 避免资源浪费:当一个服务出现故障时,如果没有熔断机制,系统可能会持续不断地尝试调用这个失败的服务,导致请求积压和资源耗尽。服务熔断通过快速失败,避免了不必要的调用,节省了宝贵的系统资源。

  2. 防止级联故障:在微服务架构中,服务之间通常相互依赖。如果一个服务出现问题,持续的失败调用可能会影响到依赖它的其他服务,导致级联故障。服务熔断器可以在问题初期及时切断受影响的服务调用,防止故障扩散到整个系统。

  3. 加速系统恢复:通过熔断机制,系统能够更快地检测到服务的故障状态,并在熔断器打开后,等待一段时间再尝试恢复调用。这有助于目标服务有足够的时间进行自我修复,从而加速整个系统的恢复过程。

  4. 提供更好的用户体验: 服务降级虽然能够保证核心功能的可用性,但在高负载或持续失败的情况下,用户可能会频繁遇到降级后的功能或默认响应,影响使用体验。服务熔断器通过控制调用频率和恢复策略,能够在保证必要降级的同时,减少对用户的负面影响。

6. 总结

本文,我们深入浅出地介绍了服务熔断机制,并通过Resilience4j的实战示例展示了如何在Java项目中实现这一机制。服务熔断机制是微服务架构中不可或缺的一部分,它能够有效地提升系统的健壮性和稳定性。

7. 学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

相关推荐
今天不学习明天变拉吉4 分钟前
分页查询列表每页1000条的优化
java·数据库·mysql·性能优化
green5+111 分钟前
卡码网55:右旋字符串
java·开发语言
huangsu_12320 分钟前
java+postgresql+swagger-单表批量和循环insert、delete操作(八)
java·开发语言·数据库·postgresql
uhakadotcom43 分钟前
了解指数退避算法:网络应用的必备策略
后端·面试·github
杨同学technotes1 小时前
如何优雅地使用本地缓存?
后端
Java技术小馆1 小时前
如何处理消息堆积
java·面试·架构
zzzzz3691 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”的解决方案
java·后端
灰色人生qwer1 小时前
lombok的坑
java·idea·lombok
异常君1 小时前
揭秘 Java 线程安全:从问题根源到实用解决方案
java·后端
雷渊1 小时前
如果消费者A订阅了下单消息,会消费之前的下单消息吗?还是从订阅后开始消费?
java·后端·面试