高性能Java并发编程:如何优雅地使用CompletableFuture进行异步编排

使用例子

java 复制代码
  /**
     * 收入日报数据组装
     */
    private <T extends StoreBaseStaDto> void getDoDailyIncomeReportInfo(List<OrganizeGeneralDetailVO> storeInfos
            , T params, String tenantId, List<FtbDataAnalysisReportDetails> analysisReportDetails) {
        Map<String, String> storeMap = storeInfos.stream().collect(Collectors.toMap(OrganizeGeneralDetailVO::getId, OrganizeGeneralDetailVO::getCashierStoreId));
        Map<String, OrganizeGeneralDetailVO> storeInfoMap = storeInfos.stream()
                .collect(Collectors.toMap(OrganizeGeneralDetailVO::getId, organizeGeneralDetailVO -> organizeGeneralDetailVO));
        // 门店信息
        StoreRevenueAnalysisStaDto aDTO = new StoreRevenueAnalysisStaDto();
        aDTO.setDimension(DimensionTypeEnums.MONTH);
        aDTO.setOpenStartDate(params.getOpenStartDate());
        aDTO.setOpenEndDate(params.getOpenEndDate());
        aDTO.setStartDate(params.getStartDate());
        aDTO.setEndDate(params.getEndDate());
        aDTO.setStoreMap(storeMap);
        StoreRevenueAnalysisStaDto bDTO = new StoreRevenueAnalysisStaDto();
        BeanUtils.copyProperties(aDTO, bDTO);
        bDTO.setDimension(DimensionTypeEnums.DAY);
        // 创建任务列表,每个任务都添加异常处理
        CompletableFuture<Map<String, DailyIncomeReportStoreInfoVO>> storeMonthInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreMonthInfoStaMap(aDTO));
                    } catch (Exception e) {
                        log.error("门店信息目标查询失败", e);
                        return new HashMap<>();
                    }
                }, executor);
        CompletableFuture<Map<String, DailyIncomeReportIntegratedVO>> storeDaySynthesisInfoFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDaySynthesisInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("综合信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor);
        CompletableFuture<Map<String, DailyIncomeReportEatInVO>> storeDayEatInInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayDineInInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("堂食信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportTakeAwayFoodVO>> storeDayTakeOutInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayTakeOutInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("外卖信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportCorrectionVO>> storeDayBadCommentInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayCorrectionInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("中差评信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportDeskVisitVO>> storeDayTableVisitInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayDeskVisitInfoStaMap(bDTO, tenantId));
                    } catch (Exception e) {
                        log.error("桌访信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        // 合并所有任务
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(
                storeMonthInfoStaMapFuture, storeDaySynthesisInfoFuture,
                storeDayEatInInfoStaMapFuture, storeDayTakeOutInfoStaMapFuture,
                storeDayBadCommentInfoStaMapFuture, storeDayTableVisitInfoStaMapFuture);
        try {
            // 设置超时时间,比如30秒
            allTasks.get(30, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.error("收入日报数据查询超时,已取消未完成的任务", e);
            // 取消所有未完成的任务
            storeMonthInfoStaMapFuture.cancel(true);
            storeDaySynthesisInfoFuture.cancel(true);
            storeDayEatInInfoStaMapFuture.cancel(true);
            storeDayTakeOutInfoStaMapFuture.cancel(true);
            storeDayBadCommentInfoStaMapFuture.cancel(true);
            storeDayTableVisitInfoStaMapFuture.cancel(true);
            // 根据业务需求,可以选择抛出异常或返回部分结果
            // 这里我们选择继续处理已完成的,但如果需要严格处理,可以抛出异常
        } catch (InterruptedException e) {
            log.error("收入日报数据查询被中断", e);
            Thread.currentThread().interrupt();
            // 同样取消所有任务
            storeMonthInfoStaMapFuture.cancel(true);
            storeDaySynthesisInfoFuture.cancel(true);
            storeDayEatInInfoStaMapFuture.cancel(true);
            storeDayTakeOutInfoStaMapFuture.cancel(true);
            storeDayBadCommentInfoStaMapFuture.cancel(true);
            storeDayTableVisitInfoStaMapFuture.cancel(true);
        } catch (ExecutionException e) {
            log.error("收入日报数据查询执行异常", e);
            // 由于每个任务都有异常处理,这里通常不会发生,但为了安全起见
        }
        // 安全地获取结果,避免join()抛出异常
        Map<String, DailyIncomeReportStoreInfoVO> storeMonthInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportIntegratedVO> storeDaySynthesisInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportEatInVO> storeDayEatInInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportTakeAwayFoodVO> storeDayTakeOutInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportCorrectionVO> storeDayBadCommentInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportDeskVisitVO> storeDayTableVisitInfoStaMap = new HashMap<>();
        try {
            if (!storeMonthInfoStaMapFuture.isCompletedExceptionally()) {
                storeMonthInfoStaMap = storeMonthInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取门店信息目标结果失败", e);
        }
        try {
            if (!storeDaySynthesisInfoFuture.isCompletedExceptionally()) {
                storeDaySynthesisInfoStaMap = storeDaySynthesisInfoFuture.get();
            }
        } catch (Exception e) {
            log.error("获取综合信息结果失败", e);
        }
        try {
            if (!storeDayEatInInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayEatInInfoStaMap = storeDayEatInInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取堂食信息结果失败", e);
        }
        try {
            if (!storeDayTakeOutInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayTakeOutInfoStaMap = storeDayTakeOutInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取外卖信息结果失败", e);
        }
        try {
            if (!storeDayBadCommentInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayBadCommentInfoStaMap = storeDayBadCommentInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取中差评信息结果失败", e);
        }
        try {
            if (!storeDayTableVisitInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayTableVisitInfoStaMap = storeDayTableVisitInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取桌访信息结果失败", e);
        }
        // 提前计算并缓存所有需要的数据
        Map<String, BigDecimal> areaEfficiencyCache = new HashMap<>();
        Map<String, BigDecimal> turnoverRateCache = new HashMap<>();
        // 预计算坪效
        for (Map.Entry<String, DailyIncomeReportIntegratedVO> entry : storeDaySynthesisInfoStaMap.entrySet()) {
            DailyIncomeReportIntegratedVO vo = entry.getValue();
            OrganizeGeneralDetailVO storeInfo = storeInfoMap.get(entry.getKey());
            if (storeInfo != null && vo != null) {
                vo.setStoreArea(storeInfo.getAreaNum());
                if (vo.getStoreArea() != null && vo.getTodayIncome() != null) {
                    BigDecimal areaEfficiency = NumberUtil.div(vo.getTodayIncome(), vo.getStoreArea(), 2);
                    vo.setAreaEfficiency(areaEfficiency);
                    areaEfficiencyCache.put(entry.getKey(), areaEfficiency);
                }
            }
        }
        // 预计算翻台率
        for (Map.Entry<String, DailyIncomeReportEatInVO> entry : storeDayEatInInfoStaMap.entrySet()) {
            DailyIncomeReportEatInVO vo = entry.getValue();
            OrganizeGeneralDetailVO storeInfo = storeInfoMap.get(entry.getKey());
            if (storeInfo != null && vo != null) {
                vo.setTableCount(storeInfo.getDeskCount());
                if (vo.getTableCount() != null && vo.getOrderCount() != null) {
                    BigDecimal turnoverRate = NumberUtil.div(vo.getOrderCount(), vo.getTableCount(), 2);
                    vo.setTurnoverRate(turnoverRate);
                    turnoverRateCache.put(entry.getKey(), turnoverRate);
                }
            }
        }
        // 组装最终数据
        for (FtbDataAnalysisReportDetails reportDetails : analysisReportDetails) {
            OrganizeGeneralDetailVO organizeGeneralDetailVO = storeInfoMap.get(reportDetails.getStoreId());
            if (organizeGeneralDetailVO == null) {
                // 跳过没有门店信息的数据
                continue;
            }
            DailyIncomeReportVO dailyIncomeReportVO = new DailyIncomeReportVO();
            // 门店信息目标
            DailyIncomeReportStoreInfoVO dailyIncomeReportStoreInfoVO = storeMonthInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportStoreInfoVO)) {
                dailyIncomeReportStoreInfoVO.setStoreName(reportDetails.getStoreName());
                dailyIncomeReportVO.setStoreInfo(dailyIncomeReportStoreInfoVO);
            }
            // 综合 - 直接从缓存中获取已计算的坪效
            DailyIncomeReportIntegratedVO dailyIncomeReportIntegratedVO = storeDaySynthesisInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportIntegratedVO)) {
                // 如果预计算时已经设置,这里可以直接使用
                if (dailyIncomeReportIntegratedVO.getAreaEfficiency() == null && areaEfficiencyCache.containsKey(reportDetails.getStoreId())) {
                    dailyIncomeReportIntegratedVO.setAreaEfficiency(areaEfficiencyCache.get(reportDetails.getStoreId()));
                }
                dailyIncomeReportVO.setIntegrated(dailyIncomeReportIntegratedVO);
            }
            // 堂食 - 直接从缓存中获取已计算的翻台率
            DailyIncomeReportEatInVO dailyIncomeReportEatInVO = storeDayEatInInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportEatInVO)) {
                // 如果预计算时已经设置,这里可以直接使用
                if (dailyIncomeReportEatInVO.getTurnoverRate() == null && turnoverRateCache.containsKey(reportDetails.getStoreId())) {
                    dailyIncomeReportEatInVO.setTurnoverRate(turnoverRateCache.get(reportDetails.getStoreId()));
                }
                dailyIncomeReportVO.setEatIn(dailyIncomeReportEatInVO);
            }
            // 外卖
            DailyIncomeReportTakeAwayFoodVO dailyIncomeReportTakeAwayFoodVO = storeDayTakeOutInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportTakeAwayFoodVO)) {
                dailyIncomeReportVO.setTakeAwayFood(dailyIncomeReportTakeAwayFoodVO);
            }
            // 中差评
            DailyIncomeReportCorrectionVO dailyIncomeReportCorrectionVO = storeDayBadCommentInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportCorrectionVO)) {
                dailyIncomeReportVO.setCorrection(dailyIncomeReportCorrectionVO);
            }
            // 桌访
            DailyIncomeReportDeskVisitVO dailyIncomeReportDeskVisitVO = storeDayTableVisitInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportDeskVisitVO)) {
                dailyIncomeReportVO.setDeskVisit(dailyIncomeReportDeskVisitVO);
            }
            reportDetails.setIncomeDailyReport(JSON.toJSONString(dailyIncomeReportVO));
        }
    }
java 复制代码
package com.fantaibao.util.wrappers;

import jnpf.database.util.TenantDataSourceUtil;
import jnpf.util.data.DataSourceContextHolder;
import lombok.experimental.UtilityClass;

import java.util.function.Consumer;
import java.util.function.Supplier;

@UtilityClass
public class TenantSupplierWrapper {

    public <U> U supplierWrapper(String tenantId, Supplier<U> supplier) {
        try {
            TenantDataSourceUtil.switchTenant(tenantId);
            return supplier.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            DataSourceContextHolder.clearDatasourceType();
        }
    }
}
相关推荐
indexsunny17 小时前
互联网大厂Java面试实战:基于电商场景的Spring Boot与微服务技术问答
java·spring boot·微服务·面试·hibernate·电商场景·技术问答
qq_124987075317 小时前
基于Spring Boot的电影票网上购票系统的设计与实现(源码+论文+部署+安装)
java·大数据·spring boot·后端·spring·毕业设计·计算机毕业设计
无心水17 小时前
【分布式利器:腾讯TSF】6、TSF可观测性体系建设实战:Java全链路Metrics+Tracing+Logging落地
java·分布式·架构·wpf·分布式利器·腾讯tsf·分布式利器:腾讯tsf
小鸡脚来咯17 小时前
Java字符串详解
java·开发语言
麦兜*17 小时前
【Spring Boot】 接口性能优化“十板斧”:从数据库连接到 JVM 调优的全链路提升
java·大数据·数据库·spring boot·后端·spring cloud·性能优化
廋到被风吹走17 小时前
【Spring 】Spring Security深度解析:过滤器链、认证授权架构与现代集成方案
java·spring·架构
蛐蛐蜉蝣耶17 小时前
Spring Boot实现DynamicMethodMatcherPointcut示例
java·spring boot·后端
予枫的编程笔记18 小时前
Elasticsearch聚合分析与大规模数据处理:解锁超越搜索的进阶能力
java·大数据·人工智能·分布式·后端·elasticsearch·全文检索
BD_Marathon18 小时前
PostMan简介
java