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

Spring中使用Async進(jìn)行異步功能開發(fā)實(shí)戰(zhàn)示例(大文件上傳為例)

 更新時間:2024年08月06日 14:46:26   作者:夜郎king  
本文以大文件上傳為例,首先講解在未進(jìn)行程序異步化時,程序的運(yùn)行機(jī)制和具體表現(xiàn),然后講解如何進(jìn)行異步化的改造,讓程序進(jìn)行異步執(zhí)行,通過本文不僅能讓你掌握如何進(jìn)行Event的事件開發(fā),同時還能掌握在Spring中如何進(jìn)行異步開發(fā),熟悉@Async的具體用法,感興趣的朋友一起看看吧

前言

        在之前的博客中,曾將講了在SpringBoot中如何使用Event來進(jìn)行大文件上傳的解耦,原文地址:使用SpringEvent解決WebUploader大文件上傳解耦問題,在這篇博客當(dāng)中,我們使用Event機(jī)制成功的將大文件的上傳和解析的功能進(jìn)行分離,已經(jīng)實(shí)現(xiàn)了解耦的需求。但是在真實(shí)項(xiàng)目中會存在一個問題,就是解耦是解耦了。但是我們期望程序能夠做到異步,也就是將文件的上傳和解析進(jìn)行徹底的異步化。后臺程序在接收前端請求的文件時,文件上傳完成后就結(jié)束。而對于上傳文件的處理和解析等操作則放到解析程序中。整個過程給人的感覺就是到上傳就完成了,解析則可以在后臺慢慢運(yùn)行,等待執(zhí)行完成即可。

        這里我們?nèi)匀灰源笪募蟼鳛槔?,首先講解在未進(jìn)行程序異步化的時候,程序的運(yùn)行機(jī)制和表現(xiàn)。然后講解如何進(jìn)行異步化的改造,讓程序進(jìn)行異步執(zhí)行。通過本文不僅能讓你掌握如何進(jìn)行Event的事件開發(fā),同時還能掌握在Spring中如何進(jìn)行異步開發(fā),熟悉@Async的具體用法。

一、場景再現(xiàn)

        為了能讓大家對故事的場景有更加直觀的認(rèn)識,這里我們將場景進(jìn)行再現(xiàn),讓大家看到具體的問題。帶著問題,我們一起來尋找解決辦法,這樣對前因后果更加清楚。

1、Event的同步機(jī)制

        首先我們來看一下原來的事件分離處理代碼,關(guān)鍵代碼如下:

@EventListener
public void fileUploadEventRegister(FileUploadEvent event){
	try {
		sys_user_logger.info("當(dāng)前處理線程名稱:" + Thread.currentThread().getName());
		FileEntity fileEntity = event.getFileEntity();
		if(StringUtils.isNotEmpty(fileEntity.getTablename())){
			FileUploadServiceRegisterEnum rigisterEnum = null;
			if(StringUtils.isNotBlank(fileEntity.getBizType())) {//業(yè)務(wù)類型不為空,則根據(jù)表名和業(yè)務(wù)名稱來查找執(zhí)行service
				rigisterEnum = FileUploadServiceRegisterEnum.getEnumByTableNameAndBizType(fileEntity.getTablename(), fileEntity.getBizType());
			}else {
				rigisterEnum = FileUploadServiceRegisterEnum.getEnumByTableName(fileEntity.getTablename());
			}
			if(null != rigisterEnum && StringUtils.isNotEmpty(rigisterEnum.getExecService())){
				String execService = rigisterEnum.getExecService();
				IFileUploadCallbackService service = SpringUtils.getBean(execService);
				service.process(fileEntity);
			}else{
				sys_user_logger.info("未注冊文件上傳監(jiān)聽回調(diào)處理器.");
			}
		}
	} catch (Exception e) {
		sys_user_logger.error("文件上傳事件監(jiān)聽發(fā)生錯誤.",e);
	}
}

        在這里為了讓文件處理的時間加長,我們可以把文件處理的邏輯加上一個線程的等待時間比如休眠等待35秒鐘。代碼如下:

@Override
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
public void process(FileEntity fileEntity) throws Exception {
	if(null != fileEntity && StringUtils.isNotEmpty(fileEntity.getBid())){
		String pkId = fileEntity.getBid();
		Student stu = studentService.selectStudentById(Long.valueOf(pkId));
		//System.out.println(fileEntity.getPath());
		//System.out.println(stu.getName() + "\t" + stu.getAddress());
		logger.info("開始處理........");
		Thread.sleep(35 * 1000);//休眠35秒測試
		logger.info("執(zhí)行結(jié)束");
	}
}

        在這里,我們使用Thread.sheep這個方法來讓線程進(jìn)行休眠等待。模擬文件上傳后,解析很慢的場景。由于卡頓變慢,下面的界面也一直處于等待的狀態(tài)。

        下面是原來的處理線程信息,通過日志可以看到,相關(guān)的執(zhí)行線程都是[http-nio-8080-exec-26]:

20:27:11.280 [http-nio-8080-exec-26] INFO  sys-user - [fileUploadEventRegister,32] - 當(dāng)前處理線程名稱:http-nio-8080-exec-26
20:27:11.281 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==>  Preparing: SELECT id,name,sex,birthday,address,remark,create_by,create_time,update_by,update_time FROM biz_student WHERE id=?
20:27:11.284 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==> Parameters: 1811048705298571266(Long)
20:27:11.287 [http-nio-8080-exec-26] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - <==      Total: 1
20:27:11.288 [http-nio-8080-exec-26] INFO  sys-user - [process,32] - 開始處理........
20:27:36.235 [schedule-pool-2] INFO  c.y.f.s.w.s.OnlineWebSessionManager - [validateSessions,100] - invalidation sessions...
20:27:36.240 [schedule-pool-2] DEBUG c.y.p.m.o.m.U.selectOnlineByExpired - [debug,137] - <==      Total: 0
20:27:36.240 [schedule-pool-2] INFO  c.y.f.s.w.s.OnlineWebSessionManager - [validateSessions,165] - Finished invalidation session. No sessions were stopped.
20:27:46.289 [http-nio-8080-exec-26] INFO  sys-user - [process,34] - 執(zhí)行結(jié)束

        大約經(jīng)過了40秒鐘之后,后臺執(zhí)行完成,前端的界面才響應(yīng)結(jié)束。 試想一下,如果您是操作的用戶,估計(jì)早就奪門而出了。

        使用這種方法開發(fā)完成后,可以看到,前面哪怕上傳一個很小的txt,它的處理時間都是要35秒以上。而我們的預(yù)期是這部分解析的功能是相對獨(dú)立的,在文件成功上傳后,前臺界面就可以關(guān)閉,文件解析的工作在后臺自動運(yùn)行。

二、性能優(yōu)化

        在遇到上面的性能和執(zhí)行流程的問題后,我們應(yīng)該怎么來解決這個事情呢?試想一下,可能有以下幾種方法。比如在后臺加快處理能力,加大運(yùn)算能力。當(dāng)然這種方法在遇到海量的文件處理的青提下,性能不一定有明顯的提升。是一種花了成本未必有很好的效果的做法。還有一種可能的做法就是將后臺的處理異步,文件的上傳和解析完全異步。文件上傳成功后,返回響應(yīng)給前端,然后前端可以繼續(xù)去做其它的事情,而后臺自動去做后續(xù)的文件處理的相關(guān)事宜。這是一個非常友好,也是推薦的做法。因此這里引出我們今天的主角,異步Async調(diào)用。

1、異步支持配置

        要想在spring中實(shí)現(xiàn)異步的支持,有很多種方式,這里介紹一種比較簡單易用的方式。首先在我們的事件監(jiān)聽器中增加@Async的注解,第一版代碼如下:

@Async
@EventListener
public void fileUploadEventRegister(FileUploadEvent event){
	try {
		// do something
	} catch (Exception e) {
		sys_user_logger.error("文件上傳事件監(jiān)聽發(fā)生錯誤.",e);
	}
}

        通過上述的代碼呢就實(shí)現(xiàn)了一個簡單的異步申明,請記住,如果只是在這里聲明這個方法是異步的,并沒有什么效果。同時還要在應(yīng)用程序的入口增加開啟異步工作的注解:

@EnableAsync

        你可以把這個注解增加到application的主入口當(dāng)中。當(dāng)然,如果到這里,其實(shí)也是可以的了。因?yàn)槲覀円呀?jīng)實(shí)現(xiàn)了整個異步工作的閉環(huán),開啟異步處理的支持,同時在方法中申明了異步的方法。上面的做法似乎是比較完美的一種做法,仔細(xì)想一下,是否真的是這樣呢?還有更好的方法嗎?可以在執(zhí)行的時候看一下,當(dāng)前方法的工作線程是什么?

2、自定義處理線程池?cái)U(kuò)展

        為了保證這個業(yè)務(wù)的可用性,其實(shí)我們可以自己定義一個線程池來執(zhí)行獨(dú)立的文件解析處理的服務(wù)。如果在應(yīng)用程序中還有其它的服務(wù)的話,彼此之間是不會不想影響的。這也是獨(dú)立線程池的好處,同時性能也是得到了大大的提升。

        那么這里將重點(diǎn)說一下如何自定義線程池,如何把線程池應(yīng)用到處理方法中呢?首先我們來定義個線程池,在Java中創(chuàng)建線程池的關(guān)鍵代碼如下:

package com.yelang.framework.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
 * 文件上傳處理線程池對象,切記:@EnableAsync一定要標(biāo)記上
 * @author 夜郎king
 *
 */
@Configuration
@EnableAsync
public class FileUploadThreadPoolConfig implements AsyncConfigurer{
	// 核心線程池大小
    private int corePoolSize = 50;
    // 最大可創(chuàng)建的線程數(shù)
    private int maxPoolSize = 200;
    // 隊(duì)列最大長度
    private int queueCapacity = 1000;
    // 線程池維護(hù)線程所允許的空閑時間
    private int keepAliveSeconds = 300;
    @Bean(name = "fileUploadTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        executor.setThreadNamePrefix("Async-FileUpload-Thread-");
        // 線程池對拒絕任務(wù)(無線程可用)的處理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
	@Override
	public Executor getAsyncExecutor() {
		return AsyncConfigurer.super.getAsyncExecutor();
	}
	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
	}
}

        在這里,我們定義了線程對象的前綴,主要是用于確定在執(zhí)行文件解析的時候,是否是使用線程池中的線程進(jìn)行解析工作的。同時我們將@EnableAsync這個注解從application的主入口轉(zhuǎn)移到了這個配置類中。請注意,這個線程池中的初始容量,最大容量,隊(duì)列長度都是固定的,實(shí)際情況下可以根據(jù)配置和任務(wù)情況進(jìn)行調(diào)整,同時設(shè)置了線程的拒絕策略。

3、將線程池配置類綁定到異步方法

        將線程池配置類定義好之后,為了讓程序能夠運(yùn)行起來,我們還需要將線程池配置類綁定到異步方法的注解中,如下所示:

//在這里指定用fileUploadTaskExecutor這個線程池去處理
@Async(value="fileUploadTaskExecutor")
@EventListener
public void fileUploadEventRegister(FileUploadEvent event){
	try {
		// do somethings
	} catch (Exception e) {
		sys_user_logger.error("文件上傳事件監(jiān)聽發(fā)生錯誤.",e);
	}
}

        在執(zhí)行的監(jiān)聽器中綁定注冊線程池之后,我們來看一下實(shí)際的執(zhí)行效果。同時主要觀察在實(shí)際的執(zhí)行過程中,是否是使用設(shè)置的線程池中的線程來進(jìn)行執(zhí)行相應(yīng)的業(yè)務(wù)的。從前臺的上傳來看,界面很快有了返回。速度是很快的。同時在后臺可以看到相關(guān)的線程處理信息,入下所示:

20:52:48.959 [Async-FileUpload-Thread-1] INFO  sys-user - [fileUploadEventRegister,32] - 當(dāng)前處理線程名稱:Async-FileUpload-Thread-1
20:52:48.967 [Async-FileUpload-Thread-1] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==>  Preparing: SELECT id,name,sex,birthday,address,remark,create_by,create_time,update_by,update_time FROM biz_student WHERE id=?
20:52:48.968 [Async-FileUpload-Thread-1] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - ==> Parameters: 1811048705298571266(Long)
20:52:48.971 [Async-FileUpload-Thread-1] DEBUG c.y.p.e.s.m.S.selectById - [debug,137] - <==      Total: 1
20:52:48.972 [Async-FileUpload-Thread-1] INFO  sys-user - [process,32] - 開始處理........
20:52:48.983 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList_COUNT - [debug,137] - ==>  Preparing: SELECT count(0) FROM biz_file WHERE (f_state = ? AND b_id = ? AND table_name = ? AND biz_type = ?)
20:52:48.984 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList_COUNT - [debug,137] - ==> Parameters: 1(Integer), 1811048705298571266(String), biz_student(String), 123a(String)
20:52:49.003 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList_COUNT - [debug,137] - <==      Total: 1
20:52:49.008 [http-nio-8080-exec-71] DEBUG c.y.p.w.m.F.selectList - [debug,137] - ==>  Preparing: SELECT id, f_id, b_id, f_type AS type, f_name AS name, f_desc AS desc, f_state AS state, f_size AS size, f_path AS path, table_name, md5code, directory, biz_type, create_by, create_time, update_by, update_time FROM biz_file WHERE (f_state = ? AND b_id = ? AND table_name = ? AND biz_type = ?) order by create_time desc LIMIT ?
20:53:23.973 [Async-FileUpload-Thread-1] INFO  sys-user - [process,34] - 執(zhí)行結(jié)束

        可以很明顯的看到,文件的解析程序是使用Async-FileUpload-Thread開頭的線程來進(jìn)行處理的,即表名是正常的使用線程池來進(jìn)行處理相關(guān)的業(yè)務(wù)。也說明的我們的設(shè)計(jì)達(dá)到了預(yù)期,即實(shí)現(xiàn)了程序的完全異步化。

三、總結(jié)

        以上就是本文的主要內(nèi)容,本文以大文件上傳為例,首先講解在未進(jìn)行程序異步化的時候,程序的運(yùn)行機(jī)制和具體表現(xiàn)。然后講解如何進(jìn)行異步化的改造,讓程序進(jìn)行異步執(zhí)行。通過本文不僅能讓你掌握如何進(jìn)行Event的事件開發(fā),同時還能掌握在Spring中如何進(jìn)行異步開發(fā),熟悉@Async的具體用法。行文倉促,難免有不足之處,如果有表達(dá)不當(dāng)?shù)牡胤交蛘卟蛔阒?,還請各位專家批評指正,在評論區(qū)留下您的真知灼見,萬分榮幸。

到此這篇關(guān)于Spring中使用Async進(jìn)行異步功能開發(fā)實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Spring Async異步開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot靜態(tài)資源(static)無法訪問問題404及解決過程

    springboot靜態(tài)資源(static)無法訪問問題404及解決過程

    文章瀏覽閱讀7.8k次。本文介紹了SpringBoot項(xiàng)目中遇到的靜態(tài)資源無法訪問的問題及解決辦法。主要從攔截器配置、靜態(tài)資源映射配置和pom.xml文件配置三個方面進(jìn)行詳細(xì)說明。
    2026-05-05
  • win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效

    win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效

    這篇文章主要介紹了win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java 獲取服務(wù)器環(huán)境的實(shí)例詳解

    Java 獲取服務(wù)器環(huán)境的實(shí)例詳解

    這篇文章主要介紹了Java 獲取服務(wù)器環(huán)境的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例和輸出結(jié)果,希望能幫助大家理解,需要的朋友可以參考下
    2017-07-07
  • springboot+Vue實(shí)現(xiàn)分頁的示例代碼

    springboot+Vue實(shí)現(xiàn)分頁的示例代碼

    本文主要介紹了springboot+Vue實(shí)現(xiàn)分頁的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-06-06
  • Dify調(diào)用Java的三種實(shí)現(xiàn)方式

    Dify調(diào)用Java的三種實(shí)現(xiàn)方式

    在一些復(fù)雜的業(yè)務(wù)中,我們可能需要使用?Dify?調(diào)用外部程序(如?Java?程序),因?yàn)檫@樣才能滿足業(yè)務(wù)的特殊需求,那么問題來了,使用?Dify?調(diào)用?Java?程序的實(shí)現(xiàn)方式有哪些呢?今天我們就來盤點(diǎn)一下,需要的朋友可以參考下
    2025-06-06
  • Win10 Java jdk14.0.2安裝及環(huán)境變量配置詳細(xì)教程

    Win10 Java jdk14.0.2安裝及環(huán)境變量配置詳細(xì)教程

    這篇文章主要介紹了Win10 Java jdk14.0.2安裝及環(huán)境變量配置,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • java代理實(shí)現(xiàn)爬取代理IP的示例

    java代理實(shí)現(xiàn)爬取代理IP的示例

    今天小編就為大家分享一篇java代理實(shí)現(xiàn)爬取代理IP的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • AJAX+JAVA用戶登陸注冊驗(yàn)證的實(shí)現(xiàn)代碼

    AJAX+JAVA用戶登陸注冊驗(yàn)證的實(shí)現(xiàn)代碼

    這篇文章主要介紹了AJAX+JAVA用戶登陸注冊驗(yàn)證的實(shí)現(xiàn)代碼,通過ajax異步刷新頁面驗(yàn)證用戶輸入的賬號密碼是否在數(shù)據(jù)庫中存在。非常具有實(shí)用價值,需要的朋友可以參考下
    2018-06-06
  • java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法

    java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法

    這篇文章主要為大家詳細(xì)介紹了java獲取網(wǎng)絡(luò)圖片上傳到OSS,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 檢查Java環(huán)境變量是否配置成功的兩種方法

    檢查Java環(huán)境變量是否配置成功的兩種方法

    本文詳細(xì)介紹如何配置Java環(huán)境變量,包括設(shè)置JAVA_HOME路徑及添加到系統(tǒng)PATH中,確保Java命令行工具可用,同時,演示了如何使用命令行編譯和運(yùn)行Java程序,以及使用javap工具進(jìn)行反匯編,需要的朋友可以參考下
    2026-03-03

最新評論

平武县| 油尖旺区| 阿鲁科尔沁旗| 保康县| 海宁市| 中方县| 太谷县| 左权县| 遂溪县| 隆林| 松原市| 南汇区| 南皮县| 迁安市| 西藏| 黔东| 资阳市| 福州市| 博客| 灌阳县| 开平市| 泗阳县| 都安| 凤山县| 巴林右旗| 阿瓦提县| 平凉市| 威海市| 武陟县| 达日县| 马关县| 永济市| 廉江市| 阿克苏市| 丹江口市| 博白县| 垦利县| 九寨沟县| 克山县| 潢川县| 年辖:市辖区|