最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java異步導(dǎo)出的實現(xiàn)過程

 更新時間:2025年10月23日 09:18:59   作者:linsm1231  
這篇文章主要介紹了java異步導(dǎo)出的實現(xiàn)過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

背景

假設(shè)我們有一個在線學(xué)習(xí)平臺,管理員需要定期生成銷售報告,包括課程銷售情況和用戶購買情況等重要數(shù)據(jù)。然而,由于數(shù)據(jù)量較大,生成報告可能需要較長時間,并且可能會占用大量系統(tǒng)資源,從而影響用戶的使用體驗。為了解決這個問題,我們考慮采用異步導(dǎo)出的方案。

異步導(dǎo)出的工作原理是將導(dǎo)出操作放在一個異步任務(wù)中執(zhí)行,而不是立即在用戶發(fā)起導(dǎo)出請求后執(zhí)行導(dǎo)出操作。這樣一來,用戶無需等待導(dǎo)出任務(wù)完成,就可以繼續(xù)進(jìn)行其他操作,而系統(tǒng)則在后臺完成導(dǎo)出任務(wù)。

這種方案有以下優(yōu)點:

  • 提高系統(tǒng)響應(yīng)速度: 用戶發(fā)起導(dǎo)出請求后,系統(tǒng)可以立即響應(yīng)而不必等待導(dǎo)出任務(wù)完成,從而提高了系統(tǒng)的響應(yīng)速度。
  • 改善用戶體驗: 用戶無需等待導(dǎo)出任務(wù)完成,可以繼續(xù)使用系統(tǒng)進(jìn)行其他操作,這有助于提升用戶體驗。
  • 降低系統(tǒng)負(fù)載: 將耗時的導(dǎo)出操作放在異步任務(wù)中執(zhí)行,可以避免阻塞系統(tǒng)資源,從而降低系統(tǒng)的負(fù)載,確保其他用戶的操作不受影響。

異步導(dǎo)出在許多需要處理大量數(shù)據(jù)或耗時操作的場景中都非常有用,可以有效提升系統(tǒng)的性能和用戶體驗。

數(shù)據(jù)庫設(shè)計

首先我們需要設(shè)計一個保存導(dǎo)出任務(wù)的表,需要記錄流轉(zhuǎn)狀態(tài)、操作人、任務(wù)參數(shù),后續(xù)任務(wù)的創(chuàng)建、導(dǎo)出完成/失敗都需要操作這張表

CREATE TABLE `t_export_task`
(
    `id`               bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
    `task_id`          varchar(50)   NOT NULL COMMENT '任務(wù)id',
    `task_type`        tinyint(4) NOT NULL COMMENT '任務(wù)類型',
    `task_param`       varchar(1000) NOT NULL COMMENT '任務(wù)參數(shù)',
    `status`           tinyint(3) NOT NULL DEFAULT 0 COMMENT '狀態(tài) 0-處理中 1-成功 -1失敗',
    `file_url`         varchar(500)           DEFAULT NULL COMMENT '文件url',
    `remark`         varchar(200)           DEFAULT NULL COMMENT '備注',
    `create_user_id`   int(11) NOT NULL COMMENT '操作人id',
    `create_user_name` varchar(50)   NOT NULL COMMENT '操作人名稱',
    `create_time`      datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
    `update_time`      datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='導(dǎo)出任務(wù)記錄';

代碼實現(xiàn)

導(dǎo)出工具類 :

負(fù)責(zé)提交導(dǎo)出任務(wù)、取消任務(wù)以及上傳導(dǎo)出文件到OSS服務(wù)器等功能。

  • 導(dǎo)出任務(wù)線程池: 通過 ExecutorService 線程池來執(zhí)行導(dǎo)出任務(wù),并確保線程池的單例化,防止重復(fù)創(chuàng)建,提高性能。
  • 導(dǎo)出類型對應(yīng)的任務(wù): 使用 Map<Integer, CompletableFuture<String>> 來保存正在運行的導(dǎo)出任務(wù),以便后續(xù)取消任務(wù)或跟蹤任務(wù)狀態(tài)。
  • 提交導(dǎo)出任務(wù): 提交導(dǎo)出任務(wù)時,先初始化線程池,然后使用 CompletableFuture.supplyAsync() 方法執(zhí)行異步任務(wù),并在異步任務(wù)中生成導(dǎo)出文件,然后上傳到OSS服務(wù)器,最后返回導(dǎo)出文件的URL。
  • 取消任務(wù): 取消任務(wù)時,從保存的任務(wù)映射中獲取對應(yīng)的 CompletableFuture 實例,并調(diào)用 cancel() 方法取消任務(wù)。
  • 上傳文件到OSS服務(wù)器: uploadToOSS() 方法負(fù)責(zé)實際的文件上傳邏輯,將導(dǎo)出的文件上傳到OSS服務(wù)器,并返回文件的URL
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ExportTaskUtil {

    /**
     * 導(dǎo)出任務(wù)線程池
     */
    private static volatile ExecutorService executorService;

    /**
     * 導(dǎo)出類型對應(yīng)的任務(wù)
     */
    private final Map<Integer, CompletableFuture<String>> runningTasks = Maps.newConcurrentMap();

    private final ExportTaskHandlerFactory exportTaskHandlerFactory;
    private final FileUploadService fileUploadService;

    /**
     * @description 提交導(dǎo)出任務(wù)
     * @author youmu
     * @date 2024/1/26 17:58
     * @param exportTask 導(dǎo)出任務(wù)
     */
    public CompletableFuture<String> submit(ExportTask exportTask) {
        // 初始化線程池
        initThreadPool();
        CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            File exportFile = null;
            // 獲取handler
            ExportTaskHandler handler = exportTaskHandlerFactory.getHandler(exportTask.getTaskType());
            // 生成文件
            try {
                exportFile = handler.generateExportFile(exportTask.getTaskParam());
                if (exportFile == null) {
                    throw new BizException(CodeEnum.NOT_FOUND, "導(dǎo)出文件為空");
                }
                // 上傳文件到OSS服務(wù)器,獲取文件URL
                return uploadToOSS(exportFile);
            } catch (BizException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (exportFile != null) {
                    FileUtil.del(exportFile);
                }
            }
        },executorService);
        runningTasks.put(exportTask.getTaskType(), future);
        return future;
    }

    private static void initThreadPool() {
        if (executorService == null) {
            synchronized (ExportTaskUtil.class) {
                if (executorService == null) {
                    executorService = ThreadUtil.newFixedExecutor(4, "asyncExport", false);;
                }
            }
        }
    }

    /**
     * @description 取消任務(wù)
     * @author youmu
     * @date 2024/1/26 19:04
     * @param exportTask 任務(wù)
     */
    public void cancel(ExportTask exportTask) {
        CompletableFuture<String> future = runningTasks.get(exportTask.getTaskType());
        if (future != null && !future.isDone()) {
            future.cancel(true);
        }
    }

    /**
     * @description 上傳文件到OSS服務(wù)器
     * @author youmu
     * @date 2024/1/29 16:56
     */
    private String uploadToOSS(File exportFile) {
        // 實現(xiàn)文件上傳邏輯,返回文件URL
        return fileUploadService.uploadFileBySize(exportFile,"export/" + exportFile.getName());
    }


}

導(dǎo)出任務(wù)處理的工廠類以及相關(guān)的接口和枚舉定義 

導(dǎo)出任務(wù)采用來工廠+策略的設(shè)計模式,工廠模式將對象的創(chuàng)建邏輯封裝到工廠類中,策略模式將不同的行為封裝到不同的策略類中,使得代碼具有良好的可擴展性、靈活性和可維護(hù)性。

  • ExportTaskHandlerFactory: 這是一個工廠類,用于根據(jù)導(dǎo)出任務(wù)類型獲取對應(yīng)的任務(wù)處理器。在初始化時,它會將所有實現(xiàn)了 ExportTaskHandler 接口的處理器注入進(jìn)來,并根據(jù)任務(wù)類型建立起映射關(guān)系。
  • ExportTaskHandler 接口: 這是一個導(dǎo)出任務(wù)處理器的接口,定義了生成導(dǎo)出文件和獲取導(dǎo)出任務(wù)類型的方法,具體的導(dǎo)出任務(wù)處理器需要實現(xiàn)該接口。
  • ExportTaskTypeEnum 枚舉: 這是一個枚舉類型,定義了導(dǎo)出任務(wù)的類型,包括了任務(wù)類型的代碼和描述信息。
  • OrderExportHandler 類: 這是一個具體的導(dǎo)出任務(wù)處理器的實現(xiàn)類,用于處理訂單導(dǎo)出任務(wù)。它實現(xiàn)了 ExportTaskHandler 接口,根據(jù)具體業(yè)務(wù)邏輯生成導(dǎo)出文件,并提供了獲取任務(wù)類型的方法。
/**
 * @description ExportTaskHandler 工廠類
 * @author youmu
 * @date 2024/1/26 18:04
 */
@Slf4j
@Component
public class ExportTaskHandlerFactory {
    private final Map<Integer, ExportTaskHandler> handlerMap = Maps.newHashMap();

    @Autowired
    public ExportTaskHandlerFactory(List<ExportTaskHandler> handlers) {
        for (ExportTaskHandler taskHandler : handlers) {
            handlerMap.put(taskHandler.getExportType().getCode(), taskHandler);
        }
    }

    public ExportTaskHandler getHandler(Integer exportType) {
        return handlerMap.get(exportType);
    }
}
/**
 * @description ExportTaskHandler
 * @author youmu
 * @date 2024/1/26 18:06
 */
public interface ExportTaskHandler {
    File generateExportFile(String param) throws Exception;

    ExportTaskTypeEnum getExportType();

}
/**
 * @description 導(dǎo)出任務(wù)類型
 * @author youmu
 * @date 2024/1/29 11:01
 */
@AllArgsConstructor
@Getter
public enum ExportTaskTypeEnum implements IEnum<Integer, String>  {
    CROWD_PACKAGE(1, "人群包"),
    ;

    private final Integer code;
    private final String message;

}
public class OrderExportHandler implements ExportTaskHandler{
    @Override
    public File generateExportFile(String param) throws Exception {
        return null;
    }

    @Override
    public ExportTaskTypeEnum getExportType() {
        return null;
    }
}

業(yè)務(wù)調(diào)用

導(dǎo)出任務(wù)的門面類 ExportTaskFacade,它提供了一系列方法來提交、取消、重試導(dǎo)出任務(wù),并提供了查詢導(dǎo)出任務(wù)的分頁接口。

  • 提交任務(wù)(submitTask): 提交導(dǎo)出任務(wù)時,根據(jù)是否傳入 taskId 參數(shù)來判斷是新建任務(wù)還是更新任務(wù)。如果是新建任務(wù),則創(chuàng)建一個新的 ExportTask 實例并保存到數(shù)據(jù)庫中,然后調(diào)用 doSubmit 方法提交任務(wù);如果是更新任務(wù),則更新任務(wù)的狀態(tài)為正在處理,并調(diào)用 doSubmit 方法提交任務(wù)。
  • 任務(wù)提交處理(doSubmit): 使用 exportTaskUtil.submit(exportTask) 提交異步導(dǎo)出任務(wù),并定義了任務(wù)完成后的處理邏輯。如果任務(wù)執(zhí)行成功,則更新任務(wù)狀態(tài)為成功,并設(shè)置文件的URL;如果任務(wù)執(zhí)行失敗,則記錄失敗日志,并更新任務(wù)狀態(tài)為失敗,同時記錄異常信息。
  • 取消任務(wù)(cancelTask): 根據(jù)傳入的 taskId 獲取對應(yīng)的導(dǎo)出任務(wù),然后調(diào)用 exportTaskUtil.cancel(exportTask) 取消任務(wù)。
  • 重試任務(wù)(retryTask): 根據(jù)傳入的 taskId 獲取對應(yīng)的導(dǎo)出任務(wù),先取消任務(wù)以防止異常情況,然后重新提交任務(wù)。
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ExportTaskFacade {
    private final ExportTaskService exportTaskService;
    private final UserService userService;
    private final ExportTaskUtil exportTaskUtil;

    public void submitTask(Integer exportType, String param) {
        submitTask(null,exportType,param);
    }

    public void cancelTask(Long taskId) {
        ExportTask exportTask = exportTaskService.getById(taskId);
        AssertUtils.notNull(exportTask, new BizException(CodeEnum.NOT_FOUND,"導(dǎo)出任務(wù)不存在"));
        exportTaskUtil.cancel(exportTask);
    }

    public void retryTask(Long taskId) {
        ExportTask exportTask = exportTaskService.getById(taskId);
        AssertUtils.notNull(exportTask, new BizException(CodeEnum.NOT_FOUND,"導(dǎo)出任務(wù)不存在"));
        // 取消任務(wù),防止異常情況還在執(zhí)行
        exportTaskUtil.cancel(exportTask);
        // 提交任務(wù)
        submitTask(taskId,exportTask.getTaskType(),exportTask.getTaskParam());
    }

    private void submitTask(Long taskId, Integer exportType, String param) {
        ExportTask exportTask;
        if(taskId == null) {
            // 保存導(dǎo)出任務(wù)
            exportTask = new ExportTask();
            Integer userId = AuthInfoHolder.getUserId();
            exportTask.setTaskId(CodeGenUtil.genCode(GenCodeTypeEnum.DL));
            exportTask.setCreateUserId(userId);
            exportTask.setCreateUserName(userService.findById(userId).getUserName());
            exportTask.setTaskType(exportType);
            exportTask.setTaskParam(param);
            exportTaskService.save(exportTask);
        } else {
            // 更新導(dǎo)出任務(wù)
            exportTask = exportTaskService.getById(taskId);
            exportTaskService.lambdaUpdate()
                    .eq(ExportTask::getId, exportTask.getId())
                    .set(ExportTask::getStatus, ExportStatusEnum.PROCESSING.getCode())
                    .update();
        }
        doSubmit(exportTask);
    }


    private void doSubmit(ExportTask exportTask) {
        exportTaskUtil.submit(exportTask).thenAccept(url->{
            exportTaskService.lambdaUpdate()
                    .eq(ExportTask::getId, exportTask.getId())
                    .set(ExportTask::getStatus, ExportStatusEnum.SUCCESS.getCode())
                    .update();
        }).exceptionally(ex->{
            log.error("[導(dǎo)出任務(wù)]執(zhí)行失敗,{}", exportTask.getTaskId(),ex);
            exportTaskService.lambdaUpdate()
                    .eq(ExportTask::getId, exportTask.getId())
                    .set(ExportTask::getStatus, ExportStatusEnum.FAILURE.getCode())
                    .set(ExportTask::getRemark, ex instanceof BizException ? ex.getMessage() : "未知異常")
                    .update();
            return null;
        });
    }

    public Page<ExportTaskVO> findPage(ExportTaskRequest request) {
        Page<ExportTask> page = exportTaskService.findPage(request);
        List<ExportTaskVO> voList = ConverterUtil.toVO(ExportTaskConverter.class, page.getRecords());
        Page<ExportTaskVO> pageVO = new Page<>();
        pageVO.setTotal(page.getTotal());
        pageVO.setSize(page.getSize());
        pageVO.setCurrent(page.getCurrent());
        pageVO.setPages(page.getPages());
        pageVO.setRecords(voList);
        return pageVO;
    }
}

流程圖

總結(jié)

過以上實踐,我們成功實現(xiàn)了一個輕量級的異步導(dǎo)出方案,具有以下優(yōu)點:

  • 使用線程池管理異步任務(wù),確保了任務(wù)的并發(fā)執(zhí)行和資源的合理利用。
  • 采用 CompletableFuture 實現(xiàn)異步導(dǎo)出和回調(diào)更新,簡化了異步任務(wù)的編寫和管理。
  • 使用工廠模式和策略模式實現(xiàn)導(dǎo)出任務(wù)處理器,使得系統(tǒng)具有良好的可擴展性和靈活性。

然而,這種方案也存在一些缺點:

  • 資源管理不足: 如果異步導(dǎo)出任務(wù)的并發(fā)量過大,而線程池的資源配置不足,則可能導(dǎo)致任務(wù)排隊等待執(zhí)行,影響任務(wù)的實時性和響應(yīng)速度。
  • 任務(wù)執(zhí)行效率低下: 如果導(dǎo)出任務(wù)的處理時間過長,且線程池的工作線程數(shù)量有限,則可能導(dǎo)致任務(wù)執(zhí)行效率低下,無法及時完成任務(wù),影響系統(tǒng)的整體性能。
  • 可靠性不高,無法保證任務(wù)一定會被執(zhí)行或執(zhí)行成功,特別是在系統(tǒng)故障或異常情況下。

針對這些缺點,可以考慮以下優(yōu)化方案:

  • 合理調(diào)整線程池配置: 根據(jù)系統(tǒng)的實際負(fù)載情況和性能需求,合理配置線程池的大小和工作線程數(shù)量,確保資源的有效利用和任務(wù)的及時執(zhí)行。
  • 優(yōu)化任務(wù)處理邏輯: 對任務(wù)的處理邏輯進(jìn)行優(yōu)化,盡量減少任務(wù)的執(zhí)行時間和資源消耗,提高任務(wù)的執(zhí)行效率和響應(yīng)速度。
  • 引入異步消息處理機制: 使用消息隊列或事件驅(qū)動模型來實現(xiàn)任務(wù)的異步處理,進(jìn)一步解耦任務(wù)提交和任務(wù)執(zhí)行過程,提高系統(tǒng)的可擴展性和靈活性。
  • 引入定時任務(wù)調(diào)度器: 使用定時任務(wù)調(diào)度器(如 xxl-job)來定期掃描和重試執(zhí)行異常任務(wù)。當(dāng)任務(wù)執(zhí)行時間超過一定閾值(如2小時)或者任務(wù)執(zhí)行異常時,自動觸發(fā)重試機制,保證任務(wù)的及時執(zhí)行。
  • 增加任務(wù)監(jiān)控和告警機制: 實時監(jiān)控任務(wù)的執(zhí)行情況,當(dāng)發(fā)現(xiàn)任務(wù)執(zhí)行異常或超時時,及時發(fā)送告警通知,以便運維人員及時處理和修復(fù)。

通過以上優(yōu)化方案,可以提高異步導(dǎo)出方案的可靠性和穩(wěn)定性,確保任務(wù)能夠及時執(zhí)行并完成,同時降低了系統(tǒng)的維護(hù)成本和風(fēng)險。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java終止正在運行的線程的三種方法

    Java終止正在運行的線程的三種方法

    停止一個線程意味著在任務(wù)處理完任務(wù)之前停掉正在做的操作,也就是放棄當(dāng)前的操作,停止一個線程可以用Thread.stop()方法,但最好不要用它,本文給大家介紹了Java終止正在運行的線程的三種方法,需要的朋友可以參考下
    2025-03-03
  • Maven 插件配置分層架構(gòu)深度解析

    Maven 插件配置分層架構(gòu)深度解析

    這篇文章主要介紹了Maven 插件配置分層架構(gòu)深度解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-05-05
  • idea插件之mybatis log plugin控制臺sql的問題

    idea插件之mybatis log plugin控制臺sql的問題

    這篇文章主要介紹了idea插件之mybatis log plugin控制臺sql,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn)

    詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn)

    很多人在使用MyBatis的緩存后經(jīng)常會遇到MySQL分頁查詢的顯示問題,針對于此,這里我們就來詳解Java的MyBatis框架中的緩存與緩存的使用改進(jìn),首先來回顧一下MyBatis的緩存機制與執(zhí)行:
    2016-06-06
  • SpringBoot應(yīng)用jar包啟動原理詳解

    SpringBoot應(yīng)用jar包啟動原理詳解

    本文主要介紹了SpringBoot應(yīng)用jar包啟動原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • idea+spring?boot創(chuàng)建項目的搭建全過程

    idea+spring?boot創(chuàng)建項目的搭建全過程

    Spring?Boot是Spring社區(qū)發(fā)布的一個開源項目,旨在幫助開發(fā)者快速并且更簡單的構(gòu)建項目,這篇文章主要介紹了idea+spring?boot創(chuàng)建項目的搭建全過程,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • IntelliJ IDEA 統(tǒng)一設(shè)置編碼為utf-8編碼的實現(xiàn)

    IntelliJ IDEA 統(tǒng)一設(shè)置編碼為utf-8編碼的實現(xiàn)

    這篇文章主要介紹了IntelliJ IDEA 統(tǒng)一設(shè)置編碼為utf-8編碼的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2020-06-06
  • RocketMQ源碼解析topic創(chuàng)建機制詳解

    RocketMQ源碼解析topic創(chuàng)建機制詳解

    這篇文章主要為大家介紹了RocketMQ源碼解析topic創(chuàng)建機制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • idea設(shè)置回車換行縮進(jìn)空格數(shù)為4的實現(xiàn)方式

    idea設(shè)置回車換行縮進(jìn)空格數(shù)為4的實現(xiàn)方式

    文章主要講述了解決在設(shè)置里回車換行僅縮進(jìn)兩空格的問題,通過將源代碼設(shè)置從回車兩空格改為回車四空格,解決了作者習(xí)慣上的不適
    2026-04-04
  • Spring?Boot小型項目如何使用異步任務(wù)管理器實現(xiàn)不同業(yè)務(wù)間的解耦

    Spring?Boot小型項目如何使用異步任務(wù)管理器實現(xiàn)不同業(yè)務(wù)間的解耦

    這篇文章主要介紹了Spring?Boot小型項目如何使用異步任務(wù)管理器實現(xiàn)不同業(yè)務(wù)間的解耦,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08

最新評論

赤城县| 乌兰县| 阿鲁科尔沁旗| 青龙| 周至县| 雅江县| 马关县| 青田县| 洮南市| 嘉鱼县| 白银市| 德江县| 吐鲁番市| 永仁县| 公主岭市| 武义县| 普兰店市| 华宁县| 安福县| 荆门市| 东光县| 延长县| 蓬安县| 白银市| 安新县| 通州市| 航空| 阿合奇县| 枣阳市| 定结县| 南城县| 黄骅市| 怀宁县| 疏附县| 虹口区| 林口县| 松溪县| 德兴市| 崇义县| 武清区| 手游|