SpringBoot开发双11商品服务系统 | 已完结

SpringBoot 开发双11商品服务系统:服务降级与熔断实现

下面我将展示如何使用SpringBoot结合Resilience4j实现双11商品服务系统的服务降级与熔断功能,包含完整的代码示例。

1. 项目基础配置

1.1 添加依赖

xml 复制代码
xml
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Resilience4j Starter -->
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot2</artifactId>
        <version>1.7.1</version>
    </dependency>
    
    <!-- Actuator for monitoring -->
    <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>

1.2 配置文件

yaml 复制代码
yaml
# application.yml
server:
  port: 8080
 
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
 
resilience4j:
  circuitbreaker:
    configs:
      default:
        registerHealthIndicator: true
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        permittedNumberOfCallsInHalfOpenState: 3
        automaticTransitionFromOpenToHalfOpenEnabled: true
        waitDurationInOpenState: 5s
        failureRateThreshold: 50
        eventConsumerBufferSize: 10
    instances:
      productService:
        baseConfig: default
        registerHealthIndicator: true
        slidingWindowSize: 20
        failureRateThreshold: 40
      inventoryService:
        baseConfig: default
        failureRateThreshold: 30
  
  retry:
    instances:
      default:
        maxAttempts: 3
        waitDuration: 100ms
        retryExceptions:
          - java.lang.RuntimeException
 
  ratelimiter:
    instances:
      default:
        limitForPeriod: 10
        limitRefreshPeriod: 1s
        timeoutDuration: 100ms

2. 核心业务实现

2.1 商品服务接口

scss 复制代码
java
// ProductService.java
public interface ProductService {
    /**
     * 获取商品详情
     */
    ProductDetail getProductDetail(Long productId);
    
    /**
     * 获取商品价格(模拟可能失败的服务)
     */
    BigDecimal getProductPrice(Long productId);
    
    /**
     * 获取库存信息(模拟可能超时的服务)
     */
    InventoryInfo getInventoryInfo(Long productId);
}

2.2 商品服务实现

kotlin 复制代码
java
// ProductServiceImpl.java
@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {
    
    private final InventoryClient inventoryClient;
    private final PriceClient priceClient;
    
    @Override
    public ProductDetail getProductDetail(Long productId) {
        // 模拟从数据库获取商品基本信息
        Product product = getProductFromDB(productId);
        
        // 获取价格信息(带熔断和降级)
        BigDecimal price = priceClient.getProductPrice(productId);
        
        // 获取库存信息(带熔断、降级和重试)
        InventoryInfo inventory = inventoryClient.getInventoryInfo(productId);
        
        return ProductDetail.builder()
                .productId(product.getId())
                .name(product.getName())
                .description(product.getDescription())
                .price(price)
                .stock(inventory.getAvailableStock())
                .build();
    }
    
    @Override
    @CircuitBreaker(name = "productService", fallbackMethod = "fallbackPrice")
    public BigDecimal getProductPrice(Long productId) {
        // 模拟随机失败
        if (Math.random() > 0.7) {
            throw new RuntimeException("Price service unavailable");
        }
        // 模拟耗时操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return BigDecimal.valueOf(99.99);
    }
    
    @Override
    @CircuitBreaker(name = "inventoryService", fallbackMethod = "fallbackInventory")
    @Retry(name = "default", fallbackMethod = "fallbackInventory")
    @RateLimiter(name = "default", fallbackMethod = "fallbackInventory")
    public InventoryInfo getInventoryInfo(Long productId) {
        // 模拟随机超时
        if (Math.random() > 0.8) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return new InventoryInfo(productId, 100, true);
    }
    
    // 降级方法:价格服务不可用时返回默认价格
    public BigDecimal fallbackPrice(Long productId, Exception e) {
        return BigDecimal.valueOf(99.99); // 返回默认价格
    }
    
    // 降级方法:库存服务不可用时返回默认库存
    public InventoryInfo fallbackInventory(Long productId, Exception e) {
        return new InventoryInfo(productId, 0, false); // 返回库存不足
    }
    
    // 降级方法:限流时返回提示
    public InventoryInfo fallbackInventory(Long productId) {
        return new InventoryInfo(productId, -1, false); // 返回系统繁忙
    }
    
    private Product getProductFromDB(Long productId) {
        // 模拟数据库查询
        return Product.builder()
                .id(productId)
                .name("双11特价商品-" + productId)
                .description("双11限时抢购,超值优惠")
                .build();
    }
}

2.3 Feign客户端实现(模拟外部服务调用)

less 复制代码
java
// InventoryClient.java
@FeignClient(name = "inventory-service", url = "http://localhost:8081")
public interface InventoryClient {
    @GetMapping("/inventory/{productId}")
    InventoryInfo getInventoryInfo(@PathVariable Long productId);
}
 
// PriceClient.java
@FeignClient(name = "price-service", url = "http://localhost:8082")
public interface PriceClient {
    @GetMapping("/price/{productId}")
    BigDecimal getProductPrice(@PathVariable Long productId);
}

3. 控制器层

kotlin 复制代码
java
// ProductController.java
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
    
    private final ProductService productService;
    
    @GetMapping("/{productId}")
    public ResponseEntity<ProductDetail> getProductDetail(
            @PathVariable Long productId,
            @RequestParam(required = false) Boolean useFallback) {
        
        // 模拟根据参数决定是否强制使用降级逻辑
        if (Boolean.TRUE.equals(useFallback)) {
            // 手动触发降级逻辑(实际中不需要这样,仅用于演示)
            try {
                throw new RuntimeException("Simulated failure");
            } catch (Exception e) {
                ProductDetail fallback = ProductDetail.builder()
                        .productId(productId)
                        .name("降级商品名称")
                        .description("服务降级中,请稍后再试")
                        .price(BigDecimal.ZERO)
                        .stock(0)
                        .build();
                return ResponseEntity.ok(fallback);
            }
        }
        
        return ResponseEntity.ok(productService.getProductDetail(productId));
    }
    
    // 专门测试熔断的接口
    @GetMapping("/{productId}/price")
    @CircuitBreaker(name = "productService", fallbackMethod = "fallbackPrice")
    public ResponseEntity<BigDecimal> getPrice(@PathVariable Long productId) {
        return ResponseEntity.ok(productService.getProductPrice(productId));
    }
    
    public ResponseEntity<BigDecimal> fallbackPrice(Long productId, Exception e) {
        return ResponseEntity.ok(BigDecimal.valueOf(88.88)); // 降级价格
    }
}

4. 数据模型

kotlin 复制代码
java
// Product.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private Long id;
    private String name;
    private String description;
}
 
// ProductDetail.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProductDetail {
    private Long productId;
    private String name;
    private String description;
    private BigDecimal price;
    private int stock;
}
 
// InventoryInfo.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class InventoryInfo {
    private Long productId;
    private int availableStock;
    private boolean isActive;
}

5. 全局异常处理

typescript 复制代码
java
// GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(CircuitBreakerOpenException.class)
    public ResponseEntity<Map<String, String>> handleCircuitBreakerOpen(CircuitBreakerOpenException e) {
        Map<String, String> body = new HashMap<>();
        body.put("status", "503");
        body.put("message", "服务暂时不可用,请稍后再试");
        body.put("reason", "熔断器已打开,防止级联故障");
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(body);
    }
    
    @ExceptionHandler(RequestNotPermitted.class)
    public ResponseEntity<Map<String, String>> handleRateLimiter(RequestNotPermitted e) {
        Map<String, String> body = new HashMap<>();
        body.put("status", "429");
        body.put("message", "系统繁忙,请稍后再试");
        body.put("reason", "请求速率超过限制");
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(body);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, String>> handleGeneralException(Exception e) {
        Map<String, String> body = new HashMap<>();
        body.put("status", "500");
        body.put("message", "服务器内部错误");
        body.put("error", e.getMessage());
        return ResponseEntity.internalServerError().body(body);
    }
}

6. 健康检查端点(可选)

less 复制代码
java
// HealthCheckController.java
@RestController
@RequestMapping("/health")
public class HealthCheckController {
    
    @GetMapping("/circuitbreakers")
    public ResponseEntity<Map<String, Object>> getCircuitBreakerStatus(
            @Autowired CircuitBreakerRegistry registry) {
        
        Map<String, Object> status = new HashMap<>();
        registry.getAllCircuitBreakers().forEach(cb -> {
            CircuitBreaker.Metrics metrics = cb.getMetrics();
            status.put(cb.getName(), Map.of(
                    "state", cb.getState(),
                    "failureRate", metrics.getFailureRate(),
                    "successfulCalls", metrics.getNumberOfSuccessfulCalls(),
                    "failedCalls", metrics.getNumberOfFailedCalls()
            ));
        });
        
        return ResponseEntity.ok(status);
    }
}

7. 测试与验证

7.1 测试熔断器行为

  1. 启动应用后,访问 /api/products/1 多次,观察正常响应

  2. 模拟服务失败:

    • 修改 getProductPrice 方法,将失败概率提高到 > 0.5
    • 继续访问接口,当失败率超过阈值(50%)时,熔断器会打开
    • 此时所有请求都会直接走降级逻辑
  3. 等待配置的等待时间(5秒)后,熔断器会进入半开状态

  4. 半开状态下,部分请求会被允许通过,如果成功则熔断器关闭

7.2 测试限流行为

  1. 使用压力测试工具(如JMeter)快速发送请求到 /api/products/1/inventory
  2. 当QPS超过配置的限流阈值(10次/秒)时,部分请求会返回429状态码

7.3 测试重试机制

  1. 修改 getInventoryInfo 方法,在第一次调用时抛出异常
  2. 配置了3次重试,所以请求最多会尝试3次
  3. 如果所有重试都失败,则走降级逻辑

8. 监控与告警

通过访问 /actuator 端点可以查看:

  • /actuator/health - 系统健康状态,包含熔断器状态
  • /actuator/circuitbreakers - 熔断器详细状态
  • /actuator/ratelimiters - 限流器状态

可以配置Prometheus和Grafana来收集这些指标并可视化展示。

总结

本文展示了如何使用SpringBoot和Resilience4j实现双11商品服务系统的服务降级与熔断功能,包括:

  1. 熔断机制 :通过@CircuitBreaker注解保护可能失败的服务调用
  2. 降级策略:为每个关键服务提供合理的降级方案
  3. 重试机制:对可重试的失败进行自动重试
  4. 限流保护:防止系统被过多请求压垮
  5. 全局异常处理:统一处理各类异常情况
  6. 监控端点:提供系统运行状态的监控接口

这种实现方式能够有效提升商品服务系统在双11高并发场景下的稳定性和可用性,确保即使部分依赖服务出现问题,核心功能仍然可用,为用户提供良好的购物体验。

相关推荐
韩立学长3 小时前
【开题答辩实录分享】以《自然灾害隐患点管理信息系统》为例进行答辩实录分享
数据库·spring boot
我命由我123453 小时前
Spring Cloud - Spring Cloud 注册中心与服务提供者(Spring Cloud Eureka 概述、微服务快速入门、微服务应用实例)
java·spring boot·spring·spring cloud·微服务·eureka·java-ee
一线大码5 小时前
SpringBoot 优雅实现接口的多实现类方式
java·spring boot·后端
Q_Q19632884755 小时前
python+uniapp基于微信小程序的助眠小程序
spring boot·python·小程序·django·flask·uni-app·node.js
摇滚侠5 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 属性优先级 行内写法 变量选择 笔记42
java·spring boot·笔记
摇滚侠5 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 总结 热部署 常用配置 笔记44
java·spring boot·笔记
十年小站5 小时前
一、新建一个SpringBoot3项目
java·spring boot
程序员阿达6 小时前
开题报告之基于SpringBoot框架的路面故障信息上报系统设计与实现
java·spring boot·后端
哞哞不熬夜6 小时前
JavaEE--SpringIoC
java·开发语言·spring boot·spring·java-ee·maven