SpringBoot使用異步線(xiàn)程池實(shí)現(xiàn)生產(chǎn)環(huán)境批量數(shù)據(jù)推送
前言
SpringBoot使用異步線(xiàn)程池:
1、編寫(xiě)線(xiàn)程池配置類(lèi),自定義一個(gè)線(xiàn)程池;
2、定義一個(gè)異步服務(wù);
3、使用@Async注解指向定義的線(xiàn)程池;
這里以我工作中使用過(guò)的一個(gè)案例來(lái)做描述,我所在公司是醫(yī)療行業(yè),敏感數(shù)據(jù)需要上報(bào)到某監(jiān)管平臺(tái),所以有一個(gè)定時(shí)任務(wù)在流量較小時(shí)(一般是凌晨后)執(zhí)行上報(bào)行為。但特殊時(shí)期會(huì)存在一定要在工作時(shí)間大批量上報(bào)數(shù)據(jù)的情況,且要求短時(shí)間內(nèi)就要完成,此時(shí)就考慮寫(xiě)一個(gè)專(zhuān)門(mén)的異步上報(bào)接口手動(dòng)執(zhí)行,利用線(xiàn)程池上報(bào),極大提高了速度。
編寫(xiě)線(xiàn)程池配置類(lèi)
import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; ? import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; ? /** ?* 類(lèi)名稱(chēng):ExecutorConfig ?* ******************************** ?* <p> ?* 類(lèi)描述:線(xiàn)程池配置 ?* ?* @author guoj ?* @date 2021-09-07 09:00 ?*/ @Configuration @EnableAsync @Slf4j public class ExecutorConfig { ? ? /** ? ? ?* 定義數(shù)據(jù)上報(bào)線(xiàn)程池 ? ? ?* @return ? ? ?*/ ? ? @Bean("dataCollectionExecutor") ? ? public Executor dataCollectionExecutor() { ? ? ? ? ? ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ? ? ? ? ? // 核心線(xiàn)程數(shù)量:當(dāng)前機(jī)器的核心數(shù) ? ? ? ? executor.setCorePoolSize( ? ? ? ? ? ? ? ? Runtime.getRuntime().availableProcessors()); ? ? ? ? ? // 最大線(xiàn)程數(shù) ? ? ? ? executor.setMaxPoolSize( ? ? ? ? ? ? ? ? Runtime.getRuntime().availableProcessors() * 2); ? ? ? ? ? // 隊(duì)列大小 ? ? ? ? executor.setQueueCapacity(Integer.MAX_VALUE); ? ? ? ? ? // 線(xiàn)程池中的線(xiàn)程名前綴 ? ? ? ? executor.setThreadNamePrefix("sjsb-"); ? ? ? ? ? // 拒絕策略:直接拒絕 ? ? ? ? executor.setRejectedExecutionHandler( ? ? ? ? ? ? ? ? new ThreadPoolExecutor.AbortPolicy()); ? ? ? ? ? // 執(zhí)行初始化 ? ? ? ? executor.initialize(); ? ? ? ? ? return executor; ? ? } }
PS:
1)、需要注意,這里一定要自己定義ThreadPoolTaskExecutor線(xiàn)程池,否則springboot的異步注解會(huì)執(zhí)行默認(rèn)線(xiàn)程池,存在線(xiàn)程阻塞導(dǎo)致CPU飆高及內(nèi)存溢出的風(fēng)險(xiǎn)。這一點(diǎn)可以參考阿里開(kāi)發(fā)手冊(cè),線(xiàn)程池定義這塊明確提到了這一點(diǎn);
2)、在@Bean注解中定義線(xiàn)程池名稱(chēng),后面異步注解會(huì)用到。
編寫(xiě)異步服務(wù)
/** ?* 異步方法的服務(wù), 不影響主程序運(yùn)行。 ?*/ @Service public class AsyncService { ? ? ? private final Logger log = LoggerFactory.getLogger(AsyncService.class); ? ? ? /** ? ? ?* 發(fā)送短信 ? ? ?*/ ? ? @Async("sendMsgExecutor") ? ? public void sendMsg(String access_token, Consult item, Map<String, String> configMap) { ? ? ? ? // 此處編寫(xiě)發(fā)送短信業(yè)務(wù) ? ? ? ? // 1、buildConsultData(); ? ? ? ? // 2、sendMsg(); ? ? } ? ? ? /** ? ? ?* 發(fā)送微信訂閱消息 ? ? ?*/ ? ? @Async ? ? public void sendSubscribeMsg(String access_token, Consult item, Map<String, String> configMap) { ? ? ? ? // 此處編寫(xiě)發(fā)送微信訂閱消息業(yè)務(wù) ? ? ? ? // 1、buildConsultData(); ? ? ? ? // 2、sendSubscribeMsg(); ? ? } ? ? ? /** ? ? ?* 數(shù)據(jù)并上報(bào) ? ? ?*/ ? ? @Async("dataCollectionExecutor") ? ? public void buildAndPostData(String access_token, Consult item, Map<String, String> configMap) { ? ? ? ? // 此處編寫(xiě)上報(bào)業(yè)務(wù),如拼接數(shù)據(jù),然后執(zhí)行上報(bào)。 ? ? ? ? // 1、buildConsultData(); ? ? ? ? // 2、postData(); ? ? } }
PS:
1)、以上是代碼片段,個(gè)人經(jīng)驗(yàn)認(rèn)為專(zhuān)門(mén)定義一個(gè)異步service存放各個(gè)異步方法最佳,這樣可以避免編碼時(shí)一些誤操作比如異步方法不是void或者是private修飾,導(dǎo)致@Async注解失效的情況,同時(shí)可以安排每個(gè)注解指向不同的自定義線(xiàn)程池更加靈活;
2)、@Async注解中的名稱(chēng)就是上面定義的自定義線(xiàn)程池名稱(chēng),這樣業(yè)務(wù)執(zhí)行時(shí)就會(huì)從指定線(xiàn)程池中獲取異步線(xiàn)程。
異步批量上報(bào)數(shù)據(jù)
@Autowired private AsyncService asyncService; ? /** ?* 手動(dòng)上報(bào)問(wèn)診記錄,線(xiàn)程池方式。 ?*/ public void manualUploadConsultRecordsAsync(String channel, Date startTime, Date endTime) { ? ? ? // 查詢(xún)指定時(shí)間內(nèi)的問(wèn)診記錄 ? ?List<Consult> consultList = consultService ? ? ? ?.findPaidListByChannelAndTime(channel, startTime, endTime, configMap.get("serviceId")); ? ? ?if (!CollectionUtils.isEmpty(consultList)) { ? ? ? ? ?log.debug("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手動(dòng)上報(bào)問(wèn)診記錄, 一共[{}]條", consultList.size()); ? ? ? ? ?consultList.forEach((item) -> { ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?// 異步調(diào)用,使用線(xiàn)程池。 ? ? ? ? ? ? ? ?asyncService.buildAndPostData(access_token, item, configMap); ? ? ? ? ? ?} catch (Exception ex) { ? ? ? ? ? ? ? ?log.error("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手動(dòng)上報(bào)問(wèn)診記錄發(fā)生異常: ", ex); ? ? ? ? ? ?} ? ? ? ?}); ? ?} }
總結(jié)
以上方式已經(jīng)在生產(chǎn)環(huán)境運(yùn)行,在工作時(shí)間內(nèi)執(zhí)行過(guò)很多次,一次數(shù)萬(wàn)條記錄基本是幾分鐘內(nèi)就全部上報(bào)完畢,而正常循環(huán)遍歷時(shí)一次大概需要半個(gè)小時(shí)左右。
線(xiàn)程池的使用方式往往來(lái)源于業(yè)務(wù)場(chǎng)景,如果類(lèi)似的業(yè)務(wù)不存在緊急處理的情況,大體還是以任務(wù)調(diào)度執(zhí)行為主,因?yàn)楦踩?。如果存在緊急處理的情況,那么使用SpringBoot+線(xiàn)程池的方式不僅能節(jié)省非常多的時(shí)間,且不占用主線(xiàn)程的執(zhí)行空間。
到此這篇關(guān)于SpringBoot使用異步線(xiàn)程池實(shí)現(xiàn)生產(chǎn)環(huán)境批量數(shù)據(jù)推送的文章就介紹到這了,更多相關(guān)SpringBoot 生產(chǎn)環(huán)境批量數(shù)據(jù)推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring?Boot異步線(xiàn)程間數(shù)據(jù)傳遞的四種方式
- springboot?正確的在異步線(xiàn)程中使用request的示例代碼
- SpringBoot整合MQTT并實(shí)現(xiàn)異步線(xiàn)程調(diào)用的問(wèn)題
- SpringBoot?異步線(xiàn)程間傳遞上下文方式
- Spring Boot之@Async異步線(xiàn)程池示例詳解
- SpringBoot獲取HttpServletRequest的3種方式總結(jié)
- SpringBoot詳細(xì)講解異步任務(wù)如何獲取HttpServletRequest
- 在 Spring Boot 中使用異步線(xiàn)程時(shí)的 HttpServletRequest 復(fù)用問(wèn)題記錄
相關(guān)文章
基于servlet的執(zhí)行原理與生命周期(全面解析)
下面小編就為大家分享一篇servlet的執(zhí)行原理與生命周期全面解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12基于ReentrantLock的實(shí)現(xiàn)原理講解
這篇文章主要介紹了ReentrantLock的實(shí)現(xiàn)原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09使用jdk1.8實(shí)現(xiàn)將list根據(jù)指定的值去分組的操作
這篇文章主要介紹了使用jdk1.8實(shí)現(xiàn)將list根據(jù)指定的值去分組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10Java實(shí)現(xiàn)在線(xiàn)編輯預(yù)覽office文檔詳解
PageOffice是一款在線(xiàn)的office編輯軟件,幫助Web應(yīng)用系統(tǒng)或Web網(wǎng)站實(shí)現(xiàn)用戶(hù)在線(xiàn)編輯Word、Excel、PowerPoint文檔,下面我們就來(lái)看看如何使用Java實(shí)現(xiàn)在線(xiàn)預(yù)覽office吧2024-01-01一文徹底吃透SpringMVC中的轉(zhuǎn)發(fā)和重定向
大家應(yīng)該都知道springmvc本來(lái)就會(huì)把返回的字符串作為視圖名解析,然后轉(zhuǎn)發(fā)到對(duì)應(yīng)的視圖,這篇文章主要給大家介紹了關(guān)于SpringMVC中轉(zhuǎn)發(fā)和重定向的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04Java 不使用第三方變量交換兩個(gè)變量值的四種方法詳解
這篇文章主要介紹了四種不使用第三方變量交換兩個(gè)變量值的方法。文中對(duì)于四種方法進(jìn)行了詳細(xì)的分析,需要的小伙伴們可以跟隨小編一起學(xué)習(xí)一下2021-12-12