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高并发场景下的稳定性和可用性,确保即使部分依赖服务出现问题,核心功能仍然可用,为用户提供良好的购物体验。

相关推荐
程序定小飞5 分钟前
基于springboot的健身房管理系统开发与设计
java·spring boot·后端
wxin_VXbishe16 分钟前
springboot在线课堂教学辅助系统-计算机毕业设计源码07741
java·c++·spring boot·python·spring·django·php
南山十一少43 分钟前
基于 Spring Boot 与 RabbitMQ 的分布式消息通信机制设计与实现
spring boot·分布式·java-rabbitmq
华仔啊1 小时前
开源一款 SpringBoot3 + Vue3 数据库文档工具,自动生成 Markdown/HTML
vue.js·spring boot·后端
合作小小程序员小小店2 小时前
web网页开发,在线%就业信息管理%系统,基于idea,html,layui,java,springboot,mysql。
java·前端·spring boot·后端·intellij-idea
陈果然DeepVersion2 小时前
Java大厂面试真题:从Spring Boot到AI微服务的三轮技术拷问(一)
java·spring boot·redis·微服务·kafka·面试题·oauth2
爱吃程序猿的喵3 小时前
Spring Boot 常用注解全面解析:提升开发效率的利器
java·spring boot·后端
熙客3 小时前
SpringBoot项目如何使用Log4j2+SLF4J构建日志
java·spring boot·log4j
WZTTMoon3 小时前
从 “完整对象” 视角看Spring 循环依赖
java·spring boot·后端·spring
是烟花哈5 小时前
后端开发CRUD实现
java·开发语言·spring boot·mybatis