霸王餐接口故障演练:ChaosMesh注入延迟与Java线程池拒绝策略

演练目标与环境

“吃喝不愁”App的霸王餐核销接口依赖下游优惠券服务,需验证在网络延迟突增场景下,上游服务是否具备容错能力。使用 ChaosMesh 在 Kubernetes 集群中注入网络延迟,并结合自定义 Java 线程池拒绝策略,确保系统不雪崩。

部署架构:

  • order-service(Spring Boot):调用 coupon-service
  • 两者均运行在 K8s,命名空间 eatfree-prod
  • ChaosMesh 已安装并授权。

ChaosMesh 网络延迟注入

创建 network-delay.yaml

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: delay-coupon-service
  namespace: eatfree-prod
spec:
  action: delay
  mode: one
  selector:
    namespaces:
      - eatfree-prod
    labelSelectors:
      app: order-service
  duration: "60s"
  delay:
    latency: "2s"
    correlation: "100"
    jitter: "0ms"
  target:
    selector:
      namespaces:
        - eatfree-prod
      labelSelectors:
        app: coupon-service
    mode: all

执行注入:

kubectl apply -f network-delay.yaml

效果:order-servicecoupon-service 的所有请求增加 2 秒固定延迟。
在这里插入图片描述

Java 异步调用与线程池配置

order-service 使用 CompletableFuture 异步调用下游,避免主线程阻塞:

package juwatech.cn.service;

import org.springframework.stereotype.Service;
import java.util.concurrent.*;

@Service
public class CouponAsyncClient {

    private final ExecutorService couponExecutor;

    public CouponAsyncClient() {
        this.couponExecutor = new ThreadPoolExecutor(
            10,                          // corePoolSize
            30,                          // maximumPoolSize
            60L, TimeUnit.SECONDS,       // keepAliveTime
            new LinkedBlockingQueue<>(50), // bounded queue
            new ThreadFactory() {
                private int counter = 0;
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "coupon-client-" + counter++);
                }
            },
            new CouponRejectedExecutionHandler() // 自定义拒绝策略
        );
    }

    public CompletableFuture<CouponResult> verifyCoupon(String code) {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟 HTTP 调用,实际使用 Feign 或 WebClient
            try {
                Thread.sleep(2500); // 模拟超时(>2s)
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return new CouponResult("VALID");
        }, couponExecutor);
    }

    static class CouponRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 记录拒绝事件,返回兜底结果
            System.err.println("Coupon task rejected. Queue size: " + executor.getQueue().size());
            throw new CouponRejectedException("Service busy, please retry later");
        }
    }
}

关键设计:

  • 有界队列LinkedBlockingQueue(50))防止内存溢出;
  • 自定义拒绝策略:抛出业务异常,触发降级逻辑。

Controller 层降级处理

package juwatech.cn.controller;

import juwatech.cn.service.CouponAsyncClient;
import juwatech.cn.service.CouponRejectedException;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

@RestController
public class OrderController {

    private final CouponAsyncClient couponClient;

    public OrderController(CouponAsyncClient couponClient) {
        this.couponClient = couponClient;
    }

    @PostMapping("/orders/claim")
    public ResponseEntity<?> claimFreeMeal(@RequestParam String couponCode) {
        CompletableFuture<CouponResult> future = couponClient.verifyCoupon(couponCode);

        try {
            // 设置 3 秒超时,略大于 ChaosMesh 注入的 2 秒
            CouponResult result = future.get(3000, TimeUnit.MILLISECONDS);
            if ("VALID".equals(result.getStatus())) {
                return ResponseEntity.ok("Claim success");
            }
        } catch (TimeoutException e) {
            return ResponseEntity.status(504).body("Coupon service timeout");
        } catch (ExecutionException e) {
            if (e.getCause() instanceof CouponRejectedException) {
                return ResponseEntity.status(503).body("System busy, try again later");
            }
            return ResponseEntity.status(500).body("Internal error");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return ResponseEntity.status(500).body("Interrupted");
        }

        return ResponseEntity.badRequest().body("Invalid coupon");
    }
}

故障演练结果分析

注入 2 秒延迟后:

  • 前 10~20 个请求因线程池空闲,排队等待,最终超时返回 504
  • 持续高并发下,队列迅速填满(50),新任务触发拒绝策略,返回 503
  • 主线程未阻塞,其他非相关接口(如用户信息查询)仍正常响应;
  • 无 Full GC,堆内存稳定。

若使用无界队列(如 newCachedThreadPool),则 JVM 内存会持续增长直至 OOM。

优化建议

  • 结合 Hystrix 或 Resilience4j 实现熔断;
  • 监控指标暴露:executor.getActiveCount(), executor.getQueue().size()
  • 拒绝策略可改为返回缓存数据或默认值,而非直接报错。

本文著作权归吃喝不愁app开发者团队,转载请注明出处!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐