Java熔断恢复案例

wen java案例 2

本文目录导读:

Java熔断恢复案例

  1. 项目依赖配置
  2. 配置文件 application.yml
  3. 服务类 Demo(核心示例)
  4. Controller 控制器
  5. 自定义配置类
  6. 全局异常处理
  7. 测试示例
  8. 监控和观察
  9. 使用说明

我来为您提供一个完整的Java熔断恢复案例,使用Spring Cloud Circuit Breaker(Resilience4j实现):

项目依赖配置

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Resilience4j Circuit Breaker -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
    </dependency>
    <!-- Resilience4j 核心 -->
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot2</artifactId>
        <version>1.7.1</version>
    </dependency>
    <!-- Actuator 用于监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

配置文件 application.yml

server:
  port: 8080
spring:
  application:
    name: circuit-breaker-demo
# Resilience4j 熔断器配置
resilience4j:
  circuitbreaker:
    instances:
      externalApi:
        # 环形缓冲区大小(关闭状态)
        ringBufferSizeInClosedState: 10
        # 半开状态缓冲区大小
        ringBufferSizeInHalfOpenState: 5
        # 失败率阈值
        failureRateThreshold: 50
        # 等待时间(自动从打开到半开)
        waitDurationInOpenState: 10000
        # 慢调用阈值
        slowCallDurationThreshold: 2000
        # 慢调用失败率
        slowCallRateThreshold: 50
        # 记录异常
        recordExceptions:
          - java.lang.Exception
        # 忽略的异常
        ignoreExceptions:
          - com.example.demo.exception.BusinessException
  # 超时配置
  timelimiter:
    instances:
      externalApi:
        # 超时时间
        timeoutDuration: 3s
        # 取消运行中的线程
        cancelRunningFuture: true
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always

服务类 Demo(核心示例)

package com.example.demo.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.annotation.Retry;
import java.time.LocalDateTime;
@Slf4j
@Service
public class ExternalService {
    private final RestTemplate restTemplate;
    // 模拟状态变量用于演示
    private int callCount = 0;
    private int successCount = 0;
    public ExternalService() {
        this.restTemplate = new RestTemplate();
    }
    /**
     * 带熔断器的外部调用方法
     * name 对应 yml 中的配置名称
     */
    @CircuitBreaker(name = "externalApi", fallbackMethod = "fallbackGetData")
    @Retry(name = "externalApi", fallbackMethod = "fallbackGetData")
    @RateLimiter(name = "externalApi", fallbackMethod = "fallbackGetData")
    public String getExternalData() {
        callCount++;
        log.info("=== 开始调用外部服务 (调用次数: {}), 时间: {} ===", callCount, LocalDateTime.now());
        // 模拟外部服务调用
        String result = callExternalApi();
        if (result.contains("成功")) {
            successCount++;
            log.info("调用成功,当前成功率: {}/{}", successCount, callCount);
            return "External Data Result: " + result;
        }
        throw new RuntimeException("外部服务调用失败");
    }
    /**
     * 模拟外部API调用
     * 用于演示不同的调用结果
     */
    private String callExternalApi() {
        // 这里可以替换成真实的API调用
        // 模拟不同的调用场景
        // 用于测试:每5次调用中,第3、4次会失败
        if (callCount % 5 == 3 || callCount % 5 == 4) {
            try {
                Thread.sleep(5000); // 模拟慢调用
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            throw new RuntimeException("外部服务响应超时");
        }
        // 模拟网络异常
        if (callCount > 10 && successCount < callCount / 2) {
            throw new RuntimeException("Network connection refused");
        }
        return "成功-" + callCount;
    }
    /**
     * 熔断降级方法
     */
    public String fallbackGetData(Throwable t) {
        log.error("熔断器触发,降级处理!错误信息: {}", t.getMessage());
        return "【降级响应】服务暂时不可用,请稍后重试,降级时间: " + LocalDateTime.now();
    }
    /**
     * 带自定义异常的熔断
     */
    @CircuitBreaker(name = "externalApi", fallbackMethod = "fallbackWithCustomException")
    public String getUserData(String userId) {
        if ("123".equals(userId)) {
            throw new IllegalArgumentException("用户ID不存在");
        }
        return "用户数据: " + userId;
    }
    public String fallbackWithCustomException(String userId, Throwable t) {
        if (t instanceof IllegalArgumentException) {
            return "参数错误: " + t.getMessage();
        }
        return "系统错误,请稍后重试";
    }
    /**
     * 重置熔断器状态(用于测试)
     */
    public void resetCircuitBreaker() {
        callCount = 0;
        successCount = 0;
    }
    public int getCallCount() {
        return callCount;
    }
    public int getSuccessCount() {
        return successCount;
    }
}

Controller 控制器

package com.example.demo.controller;
import com.example.demo.service.ExternalService;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import lombok.RequiredArgsConstructor;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class CircuitBreakerController {
    private final ExternalService externalService;
    private final CircuitBreakerRegistry circuitBreakerRegistry;
    /**
     * 正常调用接口
     */
    @GetMapping("/external")
    public String getExternalData() {
        return externalService.getExternalData();
    }
    /**
     * 获取熔断器状态
     */
    @GetMapping("/breaker/status")
    public String getBreakerStatus() {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalApi");
        return String.format(
            "状态: %s, 失败率: %.2f%%, 调用次数: %d", 
            circuitBreaker.getState(),
            circuitBreaker.getMetrics().getFailureRate(),
            circuitBreaker.getMetrics().getNumberOfSuccessfulCallsWithoutRetry() +
            circuitBreaker.getMetrics().getNumberOfFailedCallsWithoutRetry()
        );
    }
    /**
     * 重置熔断器
     */
    @PostMapping("/breaker/reset")
    public String resetBreaker() {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalApi");
        circuitBreaker.reset();
        externalService.resetCircuitBreaker();
        return "熔断器已重置,当前状态: " + circuitBreaker.getState();
    }
    /**
     * 手动触发打开熔断器
     */
    @PostMapping("/breaker/force-open")
    public String forceOpenBreaker() {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalApi");
        circuitBreaker.transitionToOpenState();
        return "熔断器已强制打开";
    }
    /**
     * 手动关闭熔断器
     */
    @PostMapping("/breaker/force-close")
    public String forceCloseBreaker() {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalApi");
        circuitBreaker.transitionToCloseState();
        return "熔断器已强制关闭";
    }
}

自定义配置类

package com.example.demo.config;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class CircuitBreakerConfig {
    /**
     * 编程式配置熔断器(可选,也可以使用yml配置)
     */
    @Bean
    public CircuitBreakerRegistry circuitBreakerRegistry() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)                    // 失败率阈值
            .waitDurationInOpenState(Duration.ofSeconds(10)) // 等待时间
            .ringBufferSizeInClosedState(10)             // 关闭状态缓冲区大小
            .ringBufferSizeInHalfOpenState(5)            // 半开状态缓冲区大小
            .slowCallDurationThreshold(Duration.ofSeconds(2)) // 慢调用阈值
            .build();
        return CircuitBreakerRegistry.of(config);
    }
}

全局异常处理

package com.example.demo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("系统异常: " + e.getMessage());
    }
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<String> handleBusinessException(BusinessException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .body("业务异常: " + e.getMessage());
    }
}

测试示例

package com.example.demo.controller;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CircuitBreakerIntegrationTest {
    @Autowired
    private TestRestTemplate restTemplate;
    @Test
    void testCircuitBreakerBehavior() {
        // 模拟多次调用触发熔断
        for (int i = 0; i < 20; i++) {
            ResponseEntity<String> response = 
                restTemplate.getForEntity("/api/external", String.class);
            System.out.println("第 " + (i+1) + " 次调用: " + response.getBody());
            // 分段停止
            if (i == 4 || i == 9 || i == 14 || i == 19) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        // 检查熔断器状态
        ResponseEntity<String> statusResponse = 
            restTemplate.getForEntity("/api/breaker/status", String.class);
        System.out.println("熔断器状态: " + statusResponse.getBody());
    }
}

监控和观察

添加监控端点和查看指标:

package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Counter;
@RestController
public class MetricsController {
    private final MeterRegistry meterRegistry;
    public MetricsController(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    @GetMapping("/metrics")
    public Object getMetrics() {
        return meterRegistry.getMeters().stream()
            .map(meter -> meter.getId().getName())
            .distinct()
            .collect(java.util.stream.Collectors.toList());
    }
}

使用说明

  1. 启动应用:运行主启动类即可看到效果
  2. 调用接口
    • GET /api/external - 测试熔断调用
    • GET /api/breaker/status - 查看熔断器状态
    • POST /api/breaker/reset - 重置熔断器
  3. 观察日志:通过日志观察熔断器的状态变化
  4. 访问监控http://localhost:8080/actuator/health 查看健康状态

这个案例展示了:

  • 熔断器的基本配置和使用
  • 降级回退机制
  • 动态状态管理
  • 监控指标
  • 测试示例

抱歉,评论功能暂时关闭!