防止外部API服务不可用拖垮系统的解决方案

防止外部API服务不可用拖垮系统的解决方案

在微服务架构中,系统间的API调用是常见场景。当外部API不可用时,如果没有适当的防护措施,可能会导致线程池耗尽、系统响应缓慢甚至整体崩溃。以下是一个基于Spring Boot的解决方案。

实际场景案例

假设我们有一个电商系统,需要调用外部支付服务API来处理订单支付。如果支付服务暂时不可用,我们不希望影响整个订单系统的运行。

解决方案核心技术

我们将使用以下技术来解决这个问题:

  1. 断路器模式(Circuit Breaker) - 使用Resilience4j实现
  2. 超时控制 - 避免长时间等待
  3. 线程隔离 - 防止API调用阻塞主服务
  4. 优雅降级 - 当服务不可用时提供备选方案
  5. 限流机制 - 控制请求速率

代码实现

1. 添加依赖(pom.xml)
 
 

xml

代码解读

复制代码

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

2. 配置文件(application.yml)
 
 

yaml

代码解读

复制代码

server: port: 8080 spring: application: name: order-service resilience4j: circuitbreaker: instances: paymentService: registerHealthIndicator: true slidingWindowSize: 10 minimumNumberOfCalls: 5 permittedNumberOfCallsInHalfOpenState: 3 automaticTransitionFromOpenToHalfOpenEnabled: true waitDurationInOpenState: 5s failureRateThreshold: 50 eventConsumerBufferSize: 10 timeout: instances: paymentService: timeoutDuration: 2s ratelimiter: instances: paymentService: limitForPeriod: 10 limitRefreshPeriod: 1s timeoutDuration: 3s retry: instances: paymentService: maxAttempts: 3 waitDuration: 1s enableExponentialBackoff: true exponentialBackoffMultiplier: 2 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always

3. 支付服务接口及模拟实现
 
 

java

代码解读

复制代码

package com.example.orderservice.service; import lombok.Data; public interface PaymentService { PaymentResponse processPayment(PaymentRequest request); } @Data class PaymentRequest { private String orderId; private double amount; private String paymentMethod; } @Data class PaymentResponse { private String transactionId; private String status; private String message; } package com.example.orderservice.service.impl; import com.example.orderservice.service.PaymentService; import org.springframework.stereotype.Service; import java.util.Random; import java.util.UUID; @Service public class PaymentServiceImpl implements PaymentService { private final Random random = new Random(); @Override public PaymentResponse processPayment(PaymentRequest request) { // 模拟外部API调用可能的延迟和失败 if (random.nextInt(10) < 3) { // 30%概率服务超时 try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (random.nextInt(10) < 2) { // 20%概率服务失败 throw new RuntimeException("Payment service unavailable"); } // 正常响应 PaymentResponse response = new PaymentResponse(); response.setTransactionId(UUID.randomUUID().toString()); response.setStatus("SUCCESS"); response.setMessage("Payment processed successfully"); return response; } }

4. 支付服务适配器(使用断路器等保护机制)
 
 

java

代码解读

复制代码

package com.example.orderservice.adapter; import com.example.orderservice.service.PaymentService; import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import io.github.resilience4j.retry.annotation.Retry; import io.github.resilience4j.timelimiter.annotation.TimeLimiter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; @Slf4j @Component @RequiredArgsConstructor public class PaymentServiceAdapter { private final PaymentService paymentService; @CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackProcessPayment") @TimeLimiter(name = "paymentService") @Retry(name = "paymentService") @RateLimiter(name = "paymentService") public CompletableFuture<PaymentResponse> processPayment(PaymentRequest request) { return CompletableFuture.supplyAsync(() -> { log.info("Calling payment service for order: {}", request.getOrderId()); return paymentService.processPayment(request); }); } public CompletableFuture<PaymentResponse> fallbackProcessPayment(PaymentRequest request, Exception ex) { log.error("Payment service failed: {}", ex.getMessage()); PaymentResponse fallbackResponse = new PaymentResponse(); if (ex instanceof TimeoutException) { log.warn("Payment service timeout for order: {}", request.getOrderId()); fallbackResponse.setStatus("PENDING"); fallbackResponse.setMessage("Payment is being processed, but status is unknown"); } else { log.warn("Payment service unavailable for order: {}", request.getOrderId()); fallbackResponse.setStatus("QUEUED"); fallbackResponse.setMessage("Payment queued for later processing"); } fallbackResponse.setTransactionId(UUID.randomUUID().toString() + "-fallback"); return CompletableFuture.completedFuture(fallbackResponse); } }

5. 订单服务
 
 

java

代码解读

复制代码

package com.example.orderservice.service; import com.example.orderservice.adapter.PaymentServiceAdapter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @Slf4j @Service @RequiredArgsConstructor public class OrderService { private final PaymentServiceAdapter paymentAdapter; public OrderResponse createOrder(OrderRequest request) { // 创建订单逻辑... String orderId = UUID.randomUUID().toString(); log.info("Order created: {}", orderId); // 处理支付 PaymentRequest paymentRequest = new PaymentRequest(); paymentRequest.setOrderId(orderId); paymentRequest.setAmount(request.getTotalAmount()); paymentRequest.setPaymentMethod(request.getPaymentMethod()); PaymentResponse paymentResponse; try { // 同步获取异步结果,但有超时保护 paymentResponse = paymentAdapter.processPayment(paymentRequest) .get(3, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error("Error processing payment", e); paymentResponse = new PaymentResponse(); paymentResponse.setStatus("ERROR"); paymentResponse.setMessage("Payment processing failed"); } // 组装订单响应 OrderResponse response = new OrderResponse(); response.setOrderId(orderId); response.setStatus("CREATED"); response.setPaymentStatus(paymentResponse.getStatus()); response.setPaymentMessage(paymentResponse.getMessage()); return response; } } @Data class OrderRequest { private double totalAmount; private String paymentMethod; private List<OrderItem> items; } @Data class OrderItem { private String productId; private int quantity; private double price; } @Data class OrderResponse { private String orderId; private String status; private String paymentStatus; private String paymentMessage; }

6. REST控制器
 
 

java

代码解读

复制代码

package com.example.orderservice.controller; import com.example.orderservice.service.OrderService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/orders") @RequiredArgsConstructor public class OrderController { private final OrderService orderService; @PostMapping public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) { OrderResponse response = orderService.createOrder(request); return ResponseEntity.ok(response); } }

解决方案说明

  1. 断路器模式:当支付服务连续失败达到阈值时,断路器打开,直接调用降级方法而不尝试调用实际服务
  2. 超时控制:设置2秒超时,避免长时间等待
  3. 线程隔离:使用CompletableFuture异步处理支付请求
  4. 优雅降级:提供fallback方法在服务不可用时返回备选结果
  5. 限流保护:限制每秒最多10个请求发送到支付服务

这种设计确保了即使支付API完全不可用,订单系统仍能继续工作,只是将支付请求标记为"待处理"或"已排队",可以在稍后重试。

监控端点(actuator)提供了系统健康状况和断路器状态的实时监控,方便运维人员及时发现问题。

需要根据实际业务调整配置参数,如超时时间、重试次数、限流阈值等。

猜你喜欢

转载自blog.csdn.net/sc35262/article/details/146917054
今日推荐