Java秒殺系統(tǒng)(十一):定時任務(wù)補(bǔ)充處理超時未支付的訂單

作者: 修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。



摘要:本篇博文是“Java秒殺系統(tǒng)實戰(zhàn)系列文章”的第十一篇,本篇博文我們將借助定時任務(wù)調(diào)度組件來輔助“失效超時未支付的訂單記錄”的處理,用以解決上篇博文中采用“RabbitMQ死信隊列失效處理超時未支付的訂單”的瑕疵!

內(nèi)容:上篇文章我們介紹了如何采用消息中間件RabbitMQ的死信隊列失效處理超時未支付的訂單,實戰(zhàn)完畢之后,相信各位小伙伴對死信隊列應(yīng)該有了一個初步的認(rèn)識以及使用。在該業(yè)務(wù)場景中,雖然死信隊列可以“近乎完美”地解決那樣的需求,但是卻仍然存在著一點瑕疵,即 “當(dāng)許多訂單記錄恰好在某個TTL集中失效,準(zhǔn)備交由RabbitMQ的死信隊列進(jìn)行處理時,此時恰好RabbitMQ的服務(wù)掛掉了。。。”

RabbitMQ的服務(wù)突然掛掉,將意味著在那個時候那些本該被失效處理的訂單記錄卻遲遲沒有得到處理(因為RabbitMQ已經(jīng)不提供服務(wù)了?。m然后面重啟了RabbitMQ服務(wù),消息也重新進(jìn)行了處理,但是卻有可能在這段時間內(nèi)用戶“重新下不了單”或者其他一些意想不到的問題!

各位小伙伴看到這里,估計應(yīng)該明白個大概了!雖然會抱怨“他娘的,無緣無故,RabbitMQ的服務(wù)咋會掛掉”!抱怨歸抱怨,現(xiàn)實卻仍舊是現(xiàn)實,隱患仍然是存在的,那既然存在隱患,那就得找尋一些方法來解決。

本篇博文我們將采用“定時器”或者叫“定時任務(wù)調(diào)度”的方式來輔助處理,即我們會設(shè)定一下Cron時間,定時在數(shù)據(jù)庫中輪詢獲取status = 0(即狀態(tài)為未支付)的訂單,并更新失效掉那些時間已經(jīng)超過了TTL的訂單!(set status = -1)。下面我們進(jìn)入代碼實戰(zhàn)環(huán)節(jié):

(1) 定時器的實現(xiàn)我們暫且采用一個比較簡單的方式,即@Scheduled注解來實現(xiàn),我們將所有的簡單定時器的實現(xiàn)都放在SchedulerService服務(wù)類中,其完整源代碼如下所示:

@Service
public class SchedulerService {
private static final Logger log= LoggerFactory.getLogger(SchedulerService.class);

@Autowired
private ItemKillSuccessMapper itemKillSuccessMapper;

@Autowired
private Environment env;

//定時獲取status=0的訂單并判斷是否超過TTL,然后進(jìn)行失效
@Scheduled(cron = "0 0/30 * * * ?")
public void schedulerExpireOrders(){
try {
List<ItemKillSuccess> list=itemKillSuccessMapper.selectExpireOrders();
if (list!=null && !list.isEmpty()){
//java8的寫法
list.stream().forEach(i -> {
if (i!=null && i.getDiffTime() > env.getProperty("scheduler.expire.orders.time",Integer.class)){
itemKillSuccessMapper.expireOrder(i.getCode());
}
});
}
}catch (Exception e){
log.error("定時獲取status=0的訂單并判斷是否超過TTL,然后進(jìn)行失效-發(fā)生異常:",e.fillInStackTrace());
}
}
}

其中的遍歷,Debug是采用了Java8的Steam API。上述這種定時任務(wù)調(diào)度的寫法簡單粗暴,而且還快?。ㄊ郎衔涔o堅不摧、無快不破!)但是,快歸快,在這里還是需要提一點注意的地方。

(2) 即如果在該項目有多個這樣的定時任務(wù),那么有一點值得注意的是:多個定時任務(wù)的執(zhí)行時間Cron如果挨得很近,那么效率一定是瓶頸,這是因為如果只是上面那樣寫,那么始終只會有一個、單一的線程在執(zhí)行上面的定時任務(wù)!

試想一下,如果有定時任務(wù)A、B、C都是每隔5分鐘執(zhí)行一次,那么很明顯,將會有一些定時任務(wù)處于阻塞的狀態(tài)而遲遲不能在規(guī)定的時間內(nèi)執(zhí)行完!

因此,為了解決此種“痛點”,我們需要加入一些自定義的配置,即針對“定時任務(wù)調(diào)度”我們加入“線程池”的配置,其完整的源代碼如下所示,各位小伙伴可以寫多幾個定時器,然后觀察控制臺的輸出信息,即可看到每個不同的定時任務(wù)采用了不同的線程執(zhí)行。

/**
* 定時任務(wù)多線程處理的通用化配置
* @Author:debug (SteadyJack)
* @Date: 2019/6/29 21:45
**/
@Configuration
public class SchedulerConfig implements SchedulingConfigurer{
//針對定時任務(wù)調(diào)度-配置多線程(線程池)
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}

(3) 至此,關(guān)于定時任務(wù)輔助失效處理秒殺系統(tǒng)中“超時未支付的訂單記錄”的代碼已經(jīng)實戰(zhàn)完畢了!可以調(diào)整一下Cron的取值,然后將整個系統(tǒng)運(yùn)行在外置的Tomcat服務(wù)器,稍等片刻即可看到定時任務(wù)的執(zhí)行了,在這里就不貼圖了!  

補(bǔ)充:

1、目前,這一秒殺系統(tǒng)的整體構(gòu)建與代碼實戰(zhàn)已經(jīng)全部完成了,該秒殺系統(tǒng)對應(yīng)的視頻教程的鏈接地址為:https://www.fightjava.com/web/index/course/detail/6,可以點擊鏈接進(jìn)行試看以及學(xué)習(xí),實戰(zhàn)期間有任何問題都可以留言或者與Debug聯(lián)系、交流!

2、另外,Debug也開源了該秒殺系統(tǒng)對應(yīng)的完整的源代碼以及數(shù)據(jù)庫,其地址可以來這里下載:https://gitee.com/steadyjack/SpringBoot-SecondKill 記得Fork跟Star?。。。?/span>

3、最后,不要忘記了關(guān)注一下Debug的技術(shù)微信公眾號: