实战案例:商品详情页数据聚合服务的技术实现

项目地址:github.com/nemoob/atla...

实战案例:商品详情页数据聚合服务的技术实现

场景需求分析

业务背景

商品详情页是电商系统的核心页面之一,需要聚合来自多个服务的数据,包括商品基础信息、库存状态、用户评价、推荐商品、价格信息、促销活动等。这些数据分布在不同的微服务中,需要通过高效的数据聚合机制来提供统一的API接口。

核心业务流程

flowchart TD A[商品详情请求] --> B[参数验证] B --> C[缓存检查] C -->|缓存命中| D[返回缓存数据] C -->|缓存未命中| E[并行数据获取] E --> F[基础信息服务] E --> G[库存服务] E --> H[评价服务] E --> I[推荐服务] E --> J[价格服务] E --> K[促销服务] F --> L[数据聚合] G --> L H --> L I --> L J --> L K --> L L --> M[数据处理] M --> N[缓存更新] N --> O[返回结果]

业务需求

  1. 高性能要求:页面响应时间需控制在200ms以内
  2. 高可用性:单个服务故障不影响整体页面展示
  3. 数据一致性:确保展示数据的准确性和时效性
  4. 扩展性:支持动态添加新的数据源
  5. 容错性:具备服务降级和熔断能力

技术挑战

  1. 多服务并行调用:如何高效地并行获取多个服务的数据
  2. 数据聚合复杂性:不同服务返回的数据格式和结构差异较大
  3. 缓存策略:如何设计合理的缓存策略平衡性能和数据一致性
  4. 异常处理:如何处理部分服务不可用的情况
  5. 性能优化:如何在保证功能完整性的前提下优化响应时间

责任链模式设计思路

整体架构设计

设计原则

  1. 并行处理:支持多个数据源的并行获取
  2. 优先级控制:根据数据重要性设置处理优先级
  3. 超时控制:为每个数据源设置合理的超时时间
  4. 降级策略:支持服务降级和默认数据返回
  5. 缓存集成:内置多级缓存策略

核心代码实现

1. 商品详情上下文定义

java 复制代码
/**
 * 商品详情聚合上下文
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductDetailContext {
    
    // 请求参数
    private String productId;
    private String userId;
    private String platform; // web, mobile, app
    private String version;
    private Map<String, Object> requestParams;
    
    // 聚合结果
    private ProductDetail productDetail;
    
    // 各数据源的结果
    private Map<String, Object> dataSourceResults = new ConcurrentHashMap<>();
    
    // 执行状态
    private Map<String, DataSourceStatus> dataSourceStatus = new ConcurrentHashMap<>();
    
    // 缓存策略
    private CacheStrategy cacheStrategy;
    
    // 性能指标
    private Map<String, Long> executionTimes = new ConcurrentHashMap<>();
    
    // 错误信息
    private Map<String, String> errors = new ConcurrentHashMap<>();
    
    /**
     * 添加数据源结果
     */
    public void addDataSourceResult(String source, Object data) {
        dataSourceResults.put(source, data);
        dataSourceStatus.put(source, DataSourceStatus.SUCCESS);
    }
    
    /**
     * 获取数据源结果
     */
    @SuppressWarnings("unchecked")
    public <T> T getDataSourceResult(String source, Class<T> type) {
        Object result = dataSourceResults.get(source);
        if (result != null && type.isInstance(result)) {
            return (T) result;
        }
        return null;
    }
    
    /**
     * 记录数据源错误
     */
    public void recordDataSourceError(String source, String error) {
        errors.put(source, error);
        dataSourceStatus.put(source, DataSourceStatus.FAILED);
    }
    
    /**
     * 记录执行时间
     */
    public void recordExecutionTime(String source, long duration) {
        executionTimes.put(source, duration);
    }
    
    /**
     * 检查是否所有必需数据源都已完成
     */
    public boolean isAllRequiredDataSourcesCompleted() {
        List<String> requiredSources = Arrays.asList(
            "productInfo", "inventory", "price"
        );
        
        return requiredSources.stream()
            .allMatch(source -> dataSourceStatus.containsKey(source));
    }
    
    /**
     * 获取成功的数据源数量
     */
    public int getSuccessfulDataSourceCount() {
        return (int) dataSourceStatus.values().stream()
            .filter(status -> status == DataSourceStatus.SUCCESS)
            .count();
    }
}

/**
 * 数据源状态枚举
 */
public enum DataSourceStatus {
    PENDING("待处理"),
    PROCESSING("处理中"),
    SUCCESS("成功"),
    FAILED("失败"),
    TIMEOUT("超时");
    
    private final String description;
    
    DataSourceStatus(String description) {
        this.description = description;
    }
}

/**
 * 商品详情聚合结果
 */
@Data
@Builder
public class ProductDetail {
    
    // 基础信息
    private ProductInfo productInfo;
    
    // 库存信息
    private InventoryInfo inventoryInfo;
    
    // 价格信息
    private PriceInfo priceInfo;
    
    // 评价信息
    private ReviewInfo reviewInfo;
    
    // 推荐商品
    private List<RecommendedProduct> recommendations;
    
    // 促销信息
    private List<PromotionInfo> promotions;
    
    // 扩展信息
    private Map<String, Object> extensions;
    
    // 元数据
    private ProductDetailMetadata metadata;
}

/**
 * 商品详情元数据
 */
@Data
@Builder
public class ProductDetailMetadata {
    private long aggregationTime;
    private int dataSourceCount;
    private int successfulDataSourceCount;
    private Map<String, Long> dataSourceExecutionTimes;
    private String cacheStatus;
    private long cacheExpireTime;
}

2. 抽象数据聚合处理器

java 复制代码
/**
 * 抽象数据聚合处理器
 */
@Slf4j
public abstract class AbstractDataAggregationHandler 
        extends AbstractHandler<ProductDetailContext, ProductDetail> {
    
    @Override
    protected final boolean doHandle(Context<ProductDetailContext, ProductDetail> context) {
        ProductDetailContext productContext = context.getRequest();
        String handlerName = this.getClass().getSimpleName();
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 检查是否应该跳过
            if (shouldSkipExecution(productContext)) {
                log.debug("Skipping handler: {} for product: {}", 
                         handlerName, productContext.getProductId());
                return true;
            }
            
            // 执行数据聚合
            boolean result = executeDataAggregation(productContext);
            
            // 记录执行时间
            long duration = System.currentTimeMillis() - startTime;
            productContext.recordExecutionTime(handlerName, duration);
            
            log.debug("Handler: {} completed for product: {}, duration: {}ms, result: {}", 
                     handlerName, productContext.getProductId(), duration, result);
            
            return result;
            
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            productContext.recordExecutionTime(handlerName, duration);
            productContext.recordDataSourceError(getDataSourceName(), e.getMessage());
            
            log.error("Handler: {} failed for product: {}, duration: {}ms", 
                     handlerName, productContext.getProductId(), duration, e);
            
            // 根据是否为必需数据源决定是否继续
            return !isRequired();
        }
    }
    
    /**
     * 执行具体的数据聚合逻辑
     */
    protected abstract boolean executeDataAggregation(ProductDetailContext context) throws Exception;
    
    /**
     * 获取数据源名称
     */
    protected abstract String getDataSourceName();
    
    /**
     * 是否为必需的数据源
     */
    protected boolean isRequired() {
        return true;
    }
    
    /**
     * 是否支持并行执行
     */
    protected boolean isParallel() {
        return true;
    }
    
    /**
     * 获取处理优先级(数字越小优先级越高)
     */
    protected int getPriority() {
        return 100;
    }
    
    /**
     * 获取超时时间(毫秒)
     */
    protected long getTimeout() {
        return 1000; // 默认1秒
    }
    
    /**
     * 是否应该跳过执行
     */
    protected boolean shouldSkipExecution(ProductDetailContext context) {
        return false;
    }
    
    /**
     * 创建降级数据
     */
    protected Object createFallbackData(ProductDetailContext context) {
        return null;
    }
}

3. 缓存处理器

java 复制代码
/**
 * 缓存处理器
 */
@Component
@Slf4j
public class ProductDetailCacheHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private CacheConfiguration cacheConfig;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        String productId = context.getProductId();
        String userId = context.getUserId();
        
        // 构建缓存键
        String cacheKey = buildCacheKey(productId, userId, context.getPlatform());
        
        // 尝试从缓存获取
        ProductDetail cachedDetail = getCachedProductDetail(cacheKey);
        
        if (cachedDetail != null) {
            log.info("Cache hit for product: {}, key: {}", productId, cacheKey);
            
            // 检查缓存数据是否仍然有效
            if (isCacheDataValid(cachedDetail, context)) {
                context.setProductDetail(cachedDetail);
                context.setCacheStrategy(CacheStrategy.HIT);
                return false; // 缓存命中,终止后续处理
            } else {
                log.info("Cached data is stale for product: {}, will refresh", productId);
                // 异步刷新缓存
                asyncRefreshCache(cacheKey, context);
            }
        }
        
        // 缓存未命中,设置缓存策略
        context.setCacheStrategy(CacheStrategy.MISS);
        
        // 添加缓存更新的回调
        context.getRequestParams().put("cacheKey", cacheKey);
        
        return true; // 继续后续处理
    }
    
    @Override
    protected String getDataSourceName() {
        return "cache";
    }
    
    @Override
    protected boolean isRequired() {
        return false; // 缓存不是必需的
    }
    
    @Override
    protected int getPriority() {
        return 1; // 最高优先级
    }
    
    @Override
    protected long getTimeout() {
        return 100; // 缓存操作超时时间较短
    }
    
    /**
     * 构建缓存键
     */
    private String buildCacheKey(String productId, String userId, String platform) {
        StringBuilder keyBuilder = new StringBuilder("product_detail:");
        keyBuilder.append(productId);
        
        // 个性化缓存键(如果有用户ID)
        if (StringUtils.isNotBlank(userId)) {
            keyBuilder.append(":user:").append(userId);
        }
        
        // 平台相关缓存键
        if (StringUtils.isNotBlank(platform)) {
            keyBuilder.append(":platform:").append(platform);
        }
        
        return keyBuilder.toString();
    }
    
    /**
     * 从缓存获取商品详情
     */
    private ProductDetail getCachedProductDetail(String cacheKey) {
        try {
            Object cached = redisTemplate.opsForValue().get(cacheKey);
            if (cached instanceof ProductDetail) {
                return (ProductDetail) cached;
            }
        } catch (Exception e) {
            log.error("Failed to get cached product detail: {}", cacheKey, e);
        }
        return null;
    }
    
    /**
     * 检查缓存数据是否有效
     */
    private boolean isCacheDataValid(ProductDetail cachedDetail, ProductDetailContext context) {
        if (cachedDetail.getMetadata() == null) {
            return false;
        }
        
        long cacheTime = cachedDetail.getMetadata().getAggregationTime();
        long currentTime = System.currentTimeMillis();
        long cacheAge = currentTime - cacheTime;
        
        // 检查缓存是否过期
        long maxAge = cacheConfig.getMaxAge(context.getProductId());
        return cacheAge < maxAge;
    }
    
    /**
     * 异步刷新缓存
     */
    @Async
    private void asyncRefreshCache(String cacheKey, ProductDetailContext context) {
        try {
            // 创建新的上下文进行数据聚合
            ProductDetailContext refreshContext = ProductDetailContext.builder()
                    .productId(context.getProductId())
                    .userId(context.getUserId())
                    .platform(context.getPlatform())
                    .requestParams(new HashMap<>(context.getRequestParams()))
                    .cacheStrategy(CacheStrategy.REFRESH)
                    .build();
            
            // 执行数据聚合(跳过缓存处理器)
            // 这里需要调用聚合服务的其他处理器
            
            log.info("Async cache refresh completed for key: {}", cacheKey);
            
        } catch (Exception e) {
            log.error("Async cache refresh failed for key: {}", cacheKey, e);
        }
    }
    
    /**
     * 更新缓存
     */
    public void updateCache(ProductDetailContext context) {
        if (context.getProductDetail() == null) {
            return;
        }
        
        String cacheKey = (String) context.getRequestParams().get("cacheKey");
        if (StringUtils.isBlank(cacheKey)) {
            return;
        }
        
        try {
            // 设置缓存过期时间
            Duration expiration = cacheConfig.getExpiration(context.getProductId());
            
            redisTemplate.opsForValue().set(cacheKey, context.getProductDetail(), expiration);
            
            log.info("Updated cache for product: {}, key: {}, expiration: {}", 
                    context.getProductId(), cacheKey, expiration);
            
        } catch (Exception e) {
            log.error("Failed to update cache for key: {}", cacheKey, e);
        }
    }
}

/**
 * 缓存策略枚举
 */
public enum CacheStrategy {
    HIT("缓存命中"),
    MISS("缓存未命中"),
    REFRESH("缓存刷新"),
    BYPASS("绕过缓存");
    
    private final String description;
    
    CacheStrategy(String description) {
        this.description = description;
    }
}

4. 商品基础信息处理器

java 复制代码
/**
 * 商品基础信息处理器
 */
@Component
@Slf4j
public class ProductInfoHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private ProductInfoService productInfoService;
    
    @Autowired
    private CircuitBreaker productInfoCircuitBreaker;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        String productId = context.getProductId();
        
        try {
            // 使用熔断器保护服务调用
            ProductInfo productInfo = productInfoCircuitBreaker.executeSupplier(() -> {
                return productInfoService.getProductInfo(productId);
            });
            
            if (productInfo != null) {
                context.addDataSourceResult(getDataSourceName(), productInfo);
                log.info("Successfully fetched product info for: {}", productId);
                return true;
            } else {
                log.warn("Product info not found for: {}", productId);
                return false;
            }
            
        } catch (CallNotPermittedException e) {
            log.warn("Circuit breaker is open for product info service, using fallback data");
            
            // 使用降级数据
            ProductInfo fallbackInfo = createFallbackProductInfo(productId);
            context.addDataSourceResult(getDataSourceName(), fallbackInfo);
            
            return true;
        }
    }
    
    @Override
    protected String getDataSourceName() {
        return "productInfo";
    }
    
    @Override
    protected boolean isRequired() {
        return true; // 商品基础信息是必需的
    }
    
    @Override
    protected int getPriority() {
        return 10; // 高优先级
    }
    
    @Override
    protected long getTimeout() {
        return 800; // 800ms超时
    }
    
    /**
     * 创建降级商品信息
     */
    private ProductInfo createFallbackProductInfo(String productId) {
        return ProductInfo.builder()
                .productId(productId)
                .productName("商品信息暂时无法获取")
                .description("商品详情正在加载中,请稍后再试")
                .status(ProductStatus.UNAVAILABLE)
                .fallback(true)
                .build();
    }
}

5. 并行数据聚合处理器

java 复制代码
/**
 * 并行数据聚合处理器
 * 负责协调多个数据源的并行获取
 */
@Component
@Slf4j
public class ParallelDataAggregationHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private List<AbstractDataAggregationHandler> dataHandlers;
    
    @Autowired
    private TaskExecutor dataAggregationExecutor;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        // 过滤出支持并行执行的处理器
        List<AbstractDataAggregationHandler> parallelHandlers = dataHandlers.stream()
                .filter(handler -> handler.isParallel())
                .filter(handler -> !(handler instanceof ParallelDataAggregationHandler))
                .filter(handler -> !(handler instanceof ProductDetailCacheHandler))
                .sorted(Comparator.comparing(AbstractDataAggregationHandler::getPriority))
                .collect(Collectors.toList());
        
        if (parallelHandlers.isEmpty()) {
            log.info("No parallel handlers found for product: {}", context.getProductId());
            return true;
        }
        
        log.info("Starting parallel data aggregation for product: {}, handlers: {}", 
                context.getProductId(), parallelHandlers.size());
        
        // 创建并行任务
        List<CompletableFuture<HandlerResult>> futures = parallelHandlers.stream()
                .map(handler -> createHandlerTask(handler, context))
                .collect(Collectors.toList());
        
        // 等待所有任务完成或超时
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0]));
        
        try {
            // 设置总体超时时间
            allTasks.get(3000, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            log.warn("Parallel data aggregation timeout for product: {}", context.getProductId());
            // 取消未完成的任务
            futures.forEach(future -> future.cancel(true));
        }
        
        // 收集结果
        int successCount = 0;
        int requiredFailureCount = 0;
        
        for (int i = 0; i < futures.size(); i++) {
            CompletableFuture<HandlerResult> future = futures.get(i);
            AbstractDataAggregationHandler handler = parallelHandlers.get(i);
            
            try {
                if (future.isDone() && !future.isCancelled()) {
                    HandlerResult result = future.get();
                    if (result.isSuccess()) {
                        successCount++;
                    } else if (handler.isRequired()) {
                        requiredFailureCount++;
                    }
                }
            } catch (Exception e) {
                log.error("Failed to get result from handler: {}", 
                         handler.getClass().getSimpleName(), e);
                if (handler.isRequired()) {
                    requiredFailureCount++;
                }
            }
        }
        
        log.info("Parallel data aggregation completed for product: {}, success: {}, required failures: {}", 
                context.getProductId(), successCount, requiredFailureCount);
        
        // 如果有必需的数据源失败,返回false
        return requiredFailureCount == 0;
    }
    
    /**
     * 创建处理器任务
     */
    private CompletableFuture<HandlerResult> createHandlerTask(
            AbstractDataAggregationHandler handler, ProductDetailContext context) {
        
        return CompletableFuture.supplyAsync(() -> {
            try {
                long startTime = System.currentTimeMillis();
                
                // 创建独立的上下文副本
                Context<ProductDetailContext, ProductDetail> handlerContext = 
                        new DefaultContext<>(context);
                
                boolean success = handler.handle(handlerContext);
                
                long duration = System.currentTimeMillis() - startTime;
                
                return HandlerResult.builder()
                        .handlerName(handler.getClass().getSimpleName())
                        .success(success)
                        .duration(duration)
                        .build();
                
            } catch (Exception e) {
                log.error("Handler execution failed: {}", 
                         handler.getClass().getSimpleName(), e);
                
                return HandlerResult.builder()
                        .handlerName(handler.getClass().getSimpleName())
                        .success(false)
                        .error(e.getMessage())
                        .build();
            }
        }, dataAggregationExecutor)
        .orTimeout(handler.getTimeout(), TimeUnit.MILLISECONDS)
        .exceptionally(throwable -> {
            String errorMessage = throwable instanceof TimeoutException ? 
                    "Handler timeout" : throwable.getMessage();
            
            return HandlerResult.builder()
                    .handlerName(handler.getClass().getSimpleName())
                    .success(false)
                    .error(errorMessage)
                    .build();
        });
    }
    
    @Override
    protected String getDataSourceName() {
        return "parallelAggregation";
    }
    
    @Override
    protected boolean isRequired() {
        return true;
    }
    
    @Override
    protected boolean isParallel() {
        return false; // 自身不参与并行执行
    }
    
    @Override
    protected int getPriority() {
        return 50; // 中等优先级
    }
}

/**
 * 处理器执行结果
 */
@Data
@Builder
public class HandlerResult {
    private String handlerName;
    private boolean success;
    private long duration;
    private String error;
}

6. 其他数据源处理器

java 复制代码
/**
 * 库存信息处理器
 */
@Component
public class InventoryInfoHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private InventoryService inventoryService;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        String productId = context.getProductId();
        
        InventoryInfo inventoryInfo = inventoryService.getInventoryInfo(productId);
        if (inventoryInfo != null) {
            context.addDataSourceResult(getDataSourceName(), inventoryInfo);
            return true;
        }
        
        return false;
    }
    
    @Override
    protected String getDataSourceName() {
        return "inventory";
    }
    
    @Override
    protected boolean isRequired() {
        return true;
    }
    
    @Override
    protected int getPriority() {
        return 20;
    }
}

/**
 * 评价信息处理器
 */
@Component
public class ReviewInfoHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private ReviewService reviewService;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        String productId = context.getProductId();
        
        ReviewInfo reviewInfo = reviewService.getReviewSummary(productId);
        if (reviewInfo != null) {
            context.addDataSourceResult(getDataSourceName(), reviewInfo);
            return true;
        }
        
        // 评价信息不是必需的,即使失败也继续
        return true;
    }
    
    @Override
    protected String getDataSourceName() {
        return "review";
    }
    
    @Override
    protected boolean isRequired() {
        return false;
    }
    
    @Override
    protected int getPriority() {
        return 40;
    }
}

/**
 * 推荐商品处理器
 */
@Component
public class RecommendationHandler extends AbstractDataAggregationHandler {
    
    @Autowired
    private RecommendationService recommendationService;
    
    @Override
    protected boolean executeDataAggregation(ProductDetailContext context) throws Exception {
        String productId = context.getProductId();
        String userId = context.getUserId();
        
        List<RecommendedProduct> recommendations = 
                recommendationService.getRecommendations(productId, userId);
        
        if (recommendations != null && !recommendations.isEmpty()) {
            context.addDataSourceResult(getDataSourceName(), recommendations);
        }
        
        return true; // 推荐不是必需的
    }
    
    @Override
    protected String getDataSourceName() {
        return "recommendation";
    }
    
    @Override
    protected boolean isRequired() {
        return false;
    }
    
    @Override
    protected int getPriority() {
        return 60;
    }
    
    @Override
    protected long getTimeout() {
        return 500; // 推荐服务超时时间较短
    }
}

7. 数据聚合服务

java 复制代码
/**
 * 商品详情数据聚合服务
 */
@Service
@Slf4j
public class ProductDetailAggregationService {
    
    @Autowired
    private ChainExecutor<ProductDetailContext, ProductDetail> chainExecutor;
    
    @Autowired
    private ProductDetailCacheHandler cacheHandler;
    
    @Autowired
    private ProductDetailAssembler assembler;
    
    /**
     * 获取商品详情
     */
    public ProductDetail getProductDetail(String productId, String userId, 
                                         String platform, Map<String, Object> params) {
        
        // 创建聚合上下文
        ProductDetailContext context = ProductDetailContext.builder()
                .productId(productId)
                .userId(userId)
                .platform(platform)
                .requestParams(params != null ? params : new HashMap<>())
                .build();
        
        try {
            log.info("Starting product detail aggregation: productId={}, userId={}, platform={}", 
                    productId, userId, platform);
            
            long startTime = System.currentTimeMillis();
            
            // 执行数据聚合链
            Context<ProductDetailContext, ProductDetail> chainContext = 
                    new DefaultContext<>(context);
            
            ProductDetail result = chainExecutor.execute("product-detail-aggregation", chainContext);
            
            // 如果链执行后没有结果,进行数据组装
            if (result == null) {
                result = assembler.assembleProductDetail(context);
                context.setProductDetail(result);
            }
            
            // 更新缓存
            if (context.getCacheStrategy() != CacheStrategy.HIT) {
                cacheHandler.updateCache(context);
            }
            
            long duration = System.currentTimeMillis() - startTime;
            
            log.info("Product detail aggregation completed: productId={}, duration={}ms, " +
                    "dataSourceCount={}, successCount={}", 
                    productId, duration, 
                    context.getDataSourceResults().size(),
                    context.getSuccessfulDataSourceCount());
            
            return result;
            
        } catch (Exception e) {
            log.error("Product detail aggregation failed: productId={}", productId, e);
            
            // 返回降级数据
            return createFallbackProductDetail(productId, e.getMessage());
        }
    }
    
    /**
     * 异步获取商品详情
     */
    @Async
    public CompletableFuture<ProductDetail> getProductDetailAsync(
            String productId, String userId, String platform, Map<String, Object> params) {
        
        return CompletableFuture.supplyAsync(() -> 
                getProductDetail(productId, userId, platform, params));
    }
    
    /**
     * 批量获取商品详情
     */
    public List<ProductDetail> getProductDetailsBatch(List<String> productIds, 
                                                      String userId, String platform) {
        
        List<CompletableFuture<ProductDetail>> futures = productIds.stream()
                .map(productId -> getProductDetailAsync(productId, userId, platform, null))
                .collect(Collectors.toList());
        
        return futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }
    
    /**
     * 创建降级商品详情
     */
    private ProductDetail createFallbackProductDetail(String productId, String error) {
        return ProductDetail.builder()
                .productInfo(ProductInfo.builder()
                        .productId(productId)
                        .productName("商品信息暂时无法获取")
                        .description("系统繁忙,请稍后再试")
                        .status(ProductStatus.UNAVAILABLE)
                        .fallback(true)
                        .build())
                .metadata(ProductDetailMetadata.builder()
                        .aggregationTime(System.currentTimeMillis())
                        .dataSourceCount(0)
                        .successfulDataSourceCount(0)
                        .cacheStatus("FALLBACK")
                        .build())
                .build();
    }
}

8. 数据组装器

java 复制代码
/**
 * 商品详情数据组装器
 */
@Component
@Slf4j
public class ProductDetailAssembler {
    
    /**
     * 组装商品详情
     */
    public ProductDetail assembleProductDetail(ProductDetailContext context) {
        log.info("Assembling product detail for: {}", context.getProductId());
        
        ProductDetail.ProductDetailBuilder builder = ProductDetail.builder();
        
        // 组装基础信息
        ProductInfo productInfo = context.getDataSourceResult("productInfo", ProductInfo.class);
        if (productInfo != null) {
            builder.productInfo(productInfo);
        }
        
        // 组装库存信息
        InventoryInfo inventoryInfo = context.getDataSourceResult("inventory", InventoryInfo.class);
        if (inventoryInfo != null) {
            builder.inventoryInfo(inventoryInfo);
        }
        
        // 组装价格信息
        PriceInfo priceInfo = context.getDataSourceResult("price", PriceInfo.class);
        if (priceInfo != null) {
            builder.priceInfo(priceInfo);
        }
        
        // 组装评价信息
        ReviewInfo reviewInfo = context.getDataSourceResult("review", ReviewInfo.class);
        if (reviewInfo != null) {
            builder.reviewInfo(reviewInfo);
        }
        
        // 组装推荐信息
        @SuppressWarnings("unchecked")
        List<RecommendedProduct> recommendations = 
                context.getDataSourceResult("recommendation", List.class);
        if (recommendations != null) {
            builder.recommendations(recommendations);
        }
        
        // 组装促销信息
        @SuppressWarnings("unchecked")
        List<PromotionInfo> promotions = 
                context.getDataSourceResult("promotion", List.class);
        if (promotions != null) {
            builder.promotions(promotions);
        }
        
        // 组装元数据
        ProductDetailMetadata metadata = ProductDetailMetadata.builder()
                .aggregationTime(System.currentTimeMillis())
                .dataSourceCount(context.getDataSourceResults().size())
                .successfulDataSourceCount(context.getSuccessfulDataSourceCount())
                .dataSourceExecutionTimes(new HashMap<>(context.getExecutionTimes()))
                .cacheStatus(context.getCacheStrategy() != null ? 
                        context.getCacheStrategy().name() : "UNKNOWN")
                .build();
        
        builder.metadata(metadata);
        
        ProductDetail result = builder.build();
        
        log.info("Product detail assembled: productId={}, dataSourceCount={}, successCount={}", 
                context.getProductId(), 
                metadata.getDataSourceCount(), 
                metadata.getSuccessfulDataSourceCount());
        
        return result;
    }
    
    /**
     * 数据后处理
     */
    public void postProcessProductDetail(ProductDetail productDetail, ProductDetailContext context) {
        if (productDetail == null) {
            return;
        }
        
        // 数据清洗和格式化
        cleanAndFormatData(productDetail);
        
        // 个性化处理
        personalizeData(productDetail, context);
        
        // 业务规则处理
        applyBusinessRules(productDetail, context);
    }
    
    /**
     * 数据清洗和格式化
     */
    private void cleanAndFormatData(ProductDetail productDetail) {
        // 清理HTML标签
        if (productDetail.getProductInfo() != null) {
            String description = productDetail.getProductInfo().getDescription();
            if (StringUtils.isNotBlank(description)) {
                String cleanDescription = cleanHtmlTags(description);
                productDetail.getProductInfo().setDescription(cleanDescription);
            }
        }
        
        // 格式化价格
        if (productDetail.getPriceInfo() != null) {
            formatPriceInfo(productDetail.getPriceInfo());
        }
    }
    
    /**
     * 个性化处理
     */
    private void personalizeData(ProductDetail productDetail, ProductDetailContext context) {
        String userId = context.getUserId();
        if (StringUtils.isBlank(userId)) {
            return;
        }
        
        // 个性化推荐
        if (productDetail.getRecommendations() != null) {
            personalizeRecommendations(productDetail.getRecommendations(), userId);
        }
        
        // 个性化价格(会员价格等)
        if (productDetail.getPriceInfo() != null) {
            personalizePricing(productDetail.getPriceInfo(), userId);
        }
    }
    
    /**
     * 应用业务规则
     */
    private void applyBusinessRules(ProductDetail productDetail, ProductDetailContext context) {
        // 库存状态处理
        if (productDetail.getInventoryInfo() != null) {
            applyInventoryRules(productDetail.getInventoryInfo());
        }
        
        // 促销规则处理
        if (productDetail.getPromotions() != null) {
            applyPromotionRules(productDetail.getPromotions());
        }
    }
    
    // 辅助方法实现...
    private String cleanHtmlTags(String html) {
        return html.replaceAll("<[^>]*>", "");
    }
    
    private void formatPriceInfo(PriceInfo priceInfo) {
        // 价格格式化逻辑
    }
    
    private void personalizeRecommendations(List<RecommendedProduct> recommendations, String userId) {
        // 个性化推荐逻辑
    }
    
    private void personalizePricing(PriceInfo priceInfo, String userId) {
        // 个性化价格逻辑
    }
    
    private void applyInventoryRules(InventoryInfo inventoryInfo) {
        // 库存规则逻辑
    }
    
    private void applyPromotionRules(List<PromotionInfo> promotions) {
         // 促销规则逻辑
     }
 }

多服务并行调用的责任链设计

1. 并行执行架构

sequenceDiagram participant Client as 客户端 participant Aggregator as 聚合服务 participant Cache as 缓存处理器 participant Parallel as 并行处理器 participant ProductSvc as 商品服务 participant InventorySvc as 库存服务 participant ReviewSvc as 评价服务 participant RecommendSvc as 推荐服务 Client->>Aggregator: 请求商品详情 Aggregator->>Cache: 检查缓存 Cache-->>Aggregator: 缓存未命中 Aggregator->>Parallel: 启动并行处理 par 并行调用 Parallel->>ProductSvc: 获取商品信息 Parallel->>InventorySvc: 获取库存信息 Parallel->>ReviewSvc: 获取评价信息 Parallel->>RecommendSvc: 获取推荐信息 end ProductSvc-->>Parallel: 商品信息 InventorySvc-->>Parallel: 库存信息 ReviewSvc-->>Parallel: 评价信息 RecommendSvc-->>Parallel: 推荐信息 Parallel->>Aggregator: 聚合结果 Aggregator->>Cache: 更新缓存 Aggregator-->>Client: 返回商品详情

2. 线程池配置

java 复制代码
/**
 * 数据聚合线程池配置
 */
@Configuration
public class DataAggregationThreadPoolConfig {
    
    @Bean("dataAggregationExecutor")
    public TaskExecutor dataAggregationExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 核心线程数
        executor.setCorePoolSize(20);
        
        // 最大线程数
        executor.setMaxPoolSize(50);
        
        // 队列容量
        executor.setQueueCapacity(100);
        
        // 线程名前缀
        executor.setThreadNamePrefix("data-aggregation-");
        
        // 拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 线程空闲时间
        executor.setKeepAliveSeconds(60);
        
        // 允许核心线程超时
        executor.setAllowCoreThreadTimeOut(true);
        
        executor.initialize();
        return executor;
    }
    
    @Bean("fastDataExecutor")
    public TaskExecutor fastDataExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("fast-data-");
        executor.initialize();
        return executor;
    }
}

3. 超时和熔断配置

java 复制代码
/**
 * 熔断器配置
 */
@Configuration
public class CircuitBreakerConfig {
    
    @Bean
    public CircuitBreaker productInfoCircuitBreaker() {
        return CircuitBreaker.ofDefaults("productInfo");
    }
    
    @Bean
    public CircuitBreaker inventoryCircuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(50)
                .waitDurationInOpenState(Duration.ofSeconds(30))
                .slidingWindowSize(10)
                .minimumNumberOfCalls(5)
                .build();
        
        return CircuitBreaker.of("inventory", config);
    }
    
    @Bean
    public CircuitBreaker reviewCircuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(70) // 评价服务容错率更高
                .waitDurationInOpenState(Duration.ofSeconds(60))
                .build();
        
        return CircuitBreaker.of("review", config);
    }
}

数据聚合与缓存策略

1. 多级缓存架构

java 复制代码
/**
 * 多级缓存管理器
 */
@Component
public class MultiLevelCacheManager {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private final ConcurrentHashMap<String, Object> localCache = new ConcurrentHashMap<>();
    private final int LOCAL_CACHE_SIZE = 1000;
    private final long LOCAL_CACHE_TTL = 60000; // 1分钟
    
    /**
     * 获取缓存数据
     */
    public <T> T get(String key, Class<T> type) {
        // 1. 先查本地缓存
        T localResult = getFromLocalCache(key, type);
        if (localResult != null) {
            return localResult;
        }
        
        // 2. 查Redis缓存
        T redisResult = getFromRedisCache(key, type);
        if (redisResult != null) {
            // 回写本地缓存
            putToLocalCache(key, redisResult);
            return redisResult;
        }
        
        return null;
    }
    
    /**
     * 设置缓存数据
     */
    public void put(String key, Object value, Duration expiration) {
        // 同时写入本地缓存和Redis
        putToLocalCache(key, value);
        putToRedisCache(key, value, expiration);
    }
    
    /**
     * 删除缓存
     */
    public void evict(String key) {
        localCache.remove(key);
        redisTemplate.delete(key);
    }
    
    @SuppressWarnings("unchecked")
    private <T> T getFromLocalCache(String key, Class<T> type) {
        CacheEntry entry = (CacheEntry) localCache.get(key);
        if (entry != null && !entry.isExpired()) {
            return (T) entry.getValue();
        }
        return null;
    }
    
    private void putToLocalCache(String key, Object value) {
        if (localCache.size() >= LOCAL_CACHE_SIZE) {
            // 简单的LRU清理
            localCache.clear();
        }
        
        CacheEntry entry = new CacheEntry(value, System.currentTimeMillis() + LOCAL_CACHE_TTL);
        localCache.put(key, entry);
    }
    
    @SuppressWarnings("unchecked")
    private <T> T getFromRedisCache(String key, Class<T> type) {
        try {
            Object value = redisTemplate.opsForValue().get(key);
            if (value != null && type.isInstance(value)) {
                return (T) value;
            }
        } catch (Exception e) {
            log.error("Failed to get from Redis cache: {}", key, e);
        }
        return null;
    }
    
    private void putToRedisCache(String key, Object value, Duration expiration) {
        try {
            redisTemplate.opsForValue().set(key, value, expiration);
        } catch (Exception e) {
            log.error("Failed to put to Redis cache: {}", key, e);
        }
    }
    
    /**
     * 缓存条目
     */
    private static class CacheEntry {
        private final Object value;
        private final long expireTime;
        
        public CacheEntry(Object value, long expireTime) {
            this.value = value;
            this.expireTime = expireTime;
        }
        
        public Object getValue() {
            return value;
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }
}

2. 缓存策略配置

java 复制代码
/**
 * 缓存策略配置
 */
@Component
@ConfigurationProperties(prefix = "product.cache")
public class CacheConfiguration {
    
    private Map<String, CachePolicy> policies = new HashMap<>();
    
    /**
     * 获取缓存过期时间
     */
    public Duration getExpiration(String productId) {
        CachePolicy policy = getCachePolicy(productId);
        return Duration.ofSeconds(policy.getTtl());
    }
    
    /**
     * 获取缓存最大年龄
     */
    public long getMaxAge(String productId) {
        CachePolicy policy = getCachePolicy(productId);
        return policy.getMaxAge() * 1000; // 转换为毫秒
    }
    
    /**
     * 是否启用缓存
     */
    public boolean isCacheEnabled(String productId) {
        CachePolicy policy = getCachePolicy(productId);
        return policy.isEnabled();
    }
    
    private CachePolicy getCachePolicy(String productId) {
        // 根据商品ID或类型获取缓存策略
        String category = getProductCategory(productId);
        return policies.getOrDefault(category, getDefaultPolicy());
    }
    
    private String getProductCategory(String productId) {
        // 简化实现,实际应该根据商品信息确定类别
        return "default";
    }
    
    private CachePolicy getDefaultPolicy() {
        return CachePolicy.builder()
                .enabled(true)
                .ttl(300) // 5分钟
                .maxAge(60) // 1分钟
                .build();
    }
    
    @Data
    @Builder
    public static class CachePolicy {
        private boolean enabled;
        private int ttl; // 缓存生存时间(秒)
        private int maxAge; // 缓存最大年龄(秒)
    }
}

性能优化建议

1. 数据预加载

java 复制代码
/**
 * 数据预加载服务
 */
@Service
public class DataPreloadService {
    
    @Autowired
    private ProductDetailAggregationService aggregationService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 预加载热门商品数据
     */
    @Scheduled(fixedRate = 300000) // 每5分钟执行一次
    public void preloadHotProducts() {
        try {
            List<String> hotProductIds = getHotProductIds();
            
            log.info("Starting preload for {} hot products", hotProductIds.size());
            
            List<CompletableFuture<Void>> futures = hotProductIds.stream()
                    .map(this::preloadProductDetail)
                    .collect(Collectors.toList());
            
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .get(30, TimeUnit.SECONDS);
            
            log.info("Preload completed for hot products");
            
        } catch (Exception e) {
            log.error("Preload failed", e);
        }
    }
    
    private CompletableFuture<Void> preloadProductDetail(String productId) {
        return CompletableFuture.runAsync(() -> {
            try {
                aggregationService.getProductDetail(productId, null, "web", null);
            } catch (Exception e) {
                log.error("Failed to preload product: {}", productId, e);
            }
        });
    }
    
    private List<String> getHotProductIds() {
        // 从推荐系统或统计系统获取热门商品ID
        return Arrays.asList("product1", "product2", "product3");
    }
}

2. 数据压缩和序列化优化

java 复制代码
/**
 * 高效序列化配置
 */
@Configuration
public class SerializationConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用Kryo序列化器
        KryoRedisSerializer<Object> kryoSerializer = new KryoRedisSerializer<>(Object.class);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(kryoSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(kryoSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

/**
 * Kryo序列化器
 */
public class KryoRedisSerializer<T> implements RedisSerializer<T> {
    
    private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.setReferences(false);
        kryo.setRegistrationRequired(false);
        return kryo;
    });
    
    private final Class<T> type;
    
    public KryoRedisSerializer(Class<T> type) {
        this.type = type;
    }
    
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             Output output = new Output(baos)) {
            
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output, t);
            output.flush();
            
            return baos.toByteArray();
        } catch (Exception e) {
            throw new SerializationException("Failed to serialize", e);
        }
    }
    
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        
        try (Input input = new Input(bytes)) {
            Kryo kryo = kryoThreadLocal.get();
            return kryo.readObject(input, type);
        } catch (Exception e) {
            throw new SerializationException("Failed to deserialize", e);
        }
    }
}

常见问题解决方案

1. 部分服务不可用的降级处理

java 复制代码
/**
 * 服务降级处理器
 */
@Component
public class ServiceDegradationHandler {
    
    /**
     * 检查服务健康状态
     */
    public boolean isServiceHealthy(String serviceName) {
        // 实现服务健康检查逻辑
        return true;
    }
    
    /**
     * 获取降级数据
     */
    public Object getFallbackData(String serviceName, String productId) {
        switch (serviceName) {
            case "productInfo":
                return createFallbackProductInfo(productId);
            case "inventory":
                return createFallbackInventoryInfo(productId);
            case "review":
                return createFallbackReviewInfo(productId);
            default:
                return null;
        }
    }
    
    private ProductInfo createFallbackProductInfo(String productId) {
        return ProductInfo.builder()
                .productId(productId)
                .productName("商品信息加载中...")
                .description("详细信息正在获取,请稍后刷新")
                .status(ProductStatus.LOADING)
                .fallback(true)
                .build();
    }
    
    private InventoryInfo createFallbackInventoryInfo(String productId) {
        return InventoryInfo.builder()
                .productId(productId)
                .available(false)
                .stock(0)
                .message("库存信息获取中")
                .fallback(true)
                .build();
    }
    
    private ReviewInfo createFallbackReviewInfo(String productId) {
        return ReviewInfo.builder()
                .productId(productId)
                .averageRating(0.0)
                .totalReviews(0)
                .message("评价信息加载中")
                .fallback(true)
                .build();
    }
}

2. 缓存穿透和缓存雪崩防护

java 复制代码
/**
 * 缓存防护机制
 */
@Component
public class CacheProtectionService {
    
    private final BloomFilter<String> bloomFilter;
    private final Map<String, Object> nullValueCache = new ConcurrentHashMap<>();
    
    public CacheProtectionService() {
        // 初始化布隆过滤器
        this.bloomFilter = BloomFilter.create(
                Funnels.stringFunnel(Charset.defaultCharset()),
                1000000, // 预期插入数量
                0.01     // 误判率
        );
        
        // 预加载已知存在的商品ID
        preloadExistingProductIds();
    }
    
    /**
     * 检查商品是否可能存在
     */
    public boolean mightExist(String productId) {
        return bloomFilter.mightContain(productId);
    }
    
    /**
     * 添加商品ID到布隆过滤器
     */
    public void addProductId(String productId) {
        bloomFilter.put(productId);
    }
    
    /**
     * 缓存空值防止缓存穿透
     */
    public void cacheNullValue(String key) {
        nullValueCache.put(key, NULL_VALUE);
        
        // 设置过期时间
        CompletableFuture.delayedExecutor(5, TimeUnit.MINUTES)
                .execute(() -> nullValueCache.remove(key));
    }
    
    /**
     * 检查是否为空值缓存
     */
    public boolean isNullValue(String key) {
        return nullValueCache.containsKey(key);
    }
    
    /**
     * 添加随机过期时间防止缓存雪崩
     */
    public Duration addRandomExpiration(Duration baseDuration) {
        Random random = new Random();
        long randomSeconds = random.nextInt(300); // 0-5分钟随机
        return baseDuration.plusSeconds(randomSeconds);
    }
    
    private void preloadExistingProductIds() {
        // 从数据库加载所有存在的商品ID
        // 这里简化处理
    }
    
    private static final Object NULL_VALUE = new Object();
}

总结

通过责任链模式实现的商品详情页数据聚合服务具有以下优势:

  1. 高性能:并行调用多个服务,显著减少响应时间
  2. 高可用:单个服务故障不影响整体功能,具备完善的降级机制
  3. 可扩展:支持动态添加新的数据源,易于功能扩展
  4. 缓存优化:多级缓存策略,平衡性能和数据一致性
  5. 监控完善:提供详细的性能指标和执行追踪

在实际应用中,需要根据具体的业务场景和性能要求进行相应的调整和优化。

相关推荐
杨杨杨大侠3 小时前
实战案例:保险理赔线上审核系统的技术实现
java·spring·github
计算机毕设定制辅导-无忧学长3 小时前
MQTT 与 Java 框架集成:Spring Boot 实战(一)
java·网络·spring boot
叫我阿柒啊3 小时前
从Java全栈到Vue3实战:一次真实面试的深度复盘
java·spring boot·微服务·vue3·响应式编程·前后端分离·restful api
快乐非自愿3 小时前
掌握设计模式--模板方法模式
java·设计模式·模板方法模式
云飞云共享云桌面3 小时前
SolidWorks对电脑的硬件配置要求具体有哪些
java·服务器·前端·网络·数据库
塔子终结者3 小时前
网络安全A模块专项练习任务十解析
java·服务器·网络安全
泉城老铁4 小时前
Spring Boot中实现多线程分片下载
java·spring boot·后端
Issie74 小时前
ThreadLocal 内存泄漏详解
java
泉城老铁4 小时前
Spring Boot中实现大文件分片下载和断点续传功能
java·spring boot·后端