SpringBoot中Redisson延遲隊(duì)列的示例
場(chǎng)景:
需求:
支付的二維碼,超過(guò)兩個(gè)小時(shí)以后,如果還未支付,則自動(dòng)轉(zhuǎn)為取消支付,或者支付超時(shí)的狀態(tài)
需求分析:
1,動(dòng)態(tài)定時(shí)任務(wù):
每個(gè)支付的二維碼創(chuàng)建的時(shí)候,創(chuàng)建一個(gè)動(dòng)態(tài)的定時(shí)任務(wù),兩個(gè)小時(shí)候自動(dòng)執(zhí)行,更新支付狀態(tài),可以解決這個(gè)問(wèn)題。
(1)持久化:
如果服務(wù)重啟了,動(dòng)態(tài)定時(shí)任務(wù)會(huì)丟失,導(dǎo)致部分?jǐn)?shù)據(jù)沒(méi)辦法更新?tīng)顟B(tài)。
(2)分布式:
如果當(dāng)服務(wù)重啟時(shí),自動(dòng)掃描數(shù)據(jù),重新計(jì)算時(shí)間,再次創(chuàng)建動(dòng)態(tài)定時(shí)任務(wù)??梢越鉀Q(1)的問(wèn)題,但是當(dāng)分布式,多個(gè)節(jié)點(diǎn)的時(shí)候,都會(huì)重新加載所有的任務(wù),這樣性能上不是最優(yōu)解,只能在數(shù)據(jù)源上加上節(jié)點(diǎn)名稱,不同的服務(wù)節(jié)點(diǎn),加載屬于自己的定時(shí)任務(wù),可以解決這個(gè)問(wèn)題??偟南胂?,太麻煩了,還是算了。
2,Redisson延遲隊(duì)列
(1)持久化:隊(duì)列信息放在Redis上,服務(wù)重啟不影響。
(2)分布式:多節(jié)點(diǎn)去Redis拿去數(shù)據(jù),誰(shuí)搶到算誰(shuí)的,不會(huì)存在同一個(gè)任務(wù),多個(gè)節(jié)點(diǎn)支持。唯一不足就是過(guò)度依賴Redis,萬(wàn)一Redis崩了,那就涼涼了(那就是要把Redis配置高可用,當(dāng)前業(yè)務(wù)就不用管了)??傮w來(lái)說(shuō)還是比較好用的。
實(shí)現(xiàn)
1,創(chuàng)建延遲隊(duì)列的監(jiān)聽(tīng)任務(wù)【RedisDelayedQueueListener】,消費(fèi)延遲隊(duì)列
2,創(chuàng)建新增延遲隊(duì)列的類,用于創(chuàng)建延遲隊(duì)列
3,整體初始化,把監(jiān)聽(tīng)任務(wù)與spring綁定,掃描各個(gè)監(jiān)聽(tīng)延遲隊(duì)列的實(shí)現(xiàn)類,并開(kāi)啟單獨(dú)線程,監(jiān)聽(tīng)任務(wù)。
4,創(chuàng)建延遲任務(wù)(開(kāi)始測(cè)試使用)
連接Redis
不貼代碼了,自己在網(wǎng)上搜
監(jiān)聽(tīng)延遲隊(duì)列
接口:
/** * 隊(duì)列事件監(jiān)聽(tīng)接口,需要實(shí)現(xiàn)這個(gè)方法 * * @module * @author frank * @date 2021/8/19 10:50 */ public interface RedisDelayedQueueListener<T> { /** * 執(zhí)行方法 * * @param t */ void invoke(T t); }
實(shí)現(xiàn):
import com.sxmaps.netschool.common.redisson.RedisDelayedQueueListener; import com.sxmaps.netschool.service.vo.school.SchoolAccountPayStateReqVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 支付二維碼監(jiān)聽(tīng)器 * * @module * @author frank * @date 2021/8/19 10:49 */ @Component public class PayQCordListener implements RedisDelayedQueueListener<SchoolAccountPayStateReqVO> { private final Logger logger = LoggerFactory.getLogger(PayQCordListener.class); @Autowired private SchoolAccountService schoolAccountService; @Override public void invoke(SchoolAccountPayStateReqVO payStateReqVO) { logger.info("支付二維碼-延遲失效,內(nèi)容:{}", payStateReqVO); //處理業(yè)務(wù),更新二維碼狀態(tài) logger.info("支付二維碼-延遲失效,內(nèi)容:{},處理結(jié)果:{}", payStateReqVO,respDTO); } }
增加延遲隊(duì)列
import org.redisson.api.RBlockingQueue; import org.redisson.api.RDelayedQueue; import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** * 增加延遲信息 * * @author frank * @module * @date 2021/8/19 10:49 */ @Component public class RedisDelayedQueue { private final Logger logger = LoggerFactory.getLogger(RedisDelayedQueue.class); @Autowired RedissonClient redissonClient; /** * 添加隊(duì)列 * * @param t DTO傳輸類 * @param delay 時(shí)間數(shù)量 * @param timeUnit 時(shí)間單位 * @param <T> 泛型 */ private <T> void addQueue(T t, long delay, TimeUnit timeUnit, String queueName) { logger.info("添加延遲隊(duì)列,監(jiān)聽(tīng)名稱:{},時(shí)間:{},時(shí)間單位:{},內(nèi)容:{}" , queueName, delay, timeUnit,t); RBlockingQueue<T> blockingFairQueue = redissonClient.getBlockingQueue(queueName); RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue); delayedQueue.offer(t, delay, timeUnit); } /** * 添加隊(duì)列-秒 * * @param t DTO傳輸類 * @param delay 時(shí)間數(shù)量 * @param <T> 泛型 */ public <T> void addQueueSeconds(T t, long delay, Class<? extends RedisDelayedQueueListener> clazz) { addQueue(t, delay, TimeUnit.SECONDS, clazz.getName()); } /** * 添加隊(duì)列-分 * * @param t DTO傳輸類 * @param delay 時(shí)間數(shù)量 * @param <T> 泛型 */ public <T> void addQueueMinutes(T t, long delay, Class<? extends RedisDelayedQueueListener> clazz) { addQueue(t, delay, TimeUnit.MINUTES, clazz.getName()); } /** * 添加隊(duì)列-時(shí) * * @param t DTO傳輸類 * @param delay 時(shí)間數(shù)量 * @param <T> 泛型 */ public <T> void addQueueHours(T t, long delay, Class<? extends RedisDelayedQueueListener> clazz) { addQueue(t, delay, TimeUnit.HOURS, clazz.getName()); } /** * 添加隊(duì)列-天 * * @param t DTO傳輸類 * @param delay 時(shí)間數(shù)量 * @param <T> 泛型 */ public <T> void addQueueDays(T t, long delay, Class<? extends RedisDelayedQueueListener> clazz) { addQueue(t, delay, TimeUnit.DAYS, clazz.getName()); } }
整體初始化
import org.redisson.api.RBlockingQueue; import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.Map; /** * 初始化隊(duì)列監(jiān)聽(tīng) * * @module * @author frank * @date 2021/8/19 10:49 */ @Component public class RedisDelayedQueueInit implements ApplicationContextAware { private final Logger logger = LoggerFactory.getLogger(RedisDelayedQueueInit.class); @Autowired RedissonClient redissonClient; /** * 獲取應(yīng)用上下文并獲取相應(yīng)的接口實(shí)現(xiàn)類 * * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map<String, RedisDelayedQueueListener> map = applicationContext.getBeansOfType(RedisDelayedQueueListener.class); for (Map.Entry<String, RedisDelayedQueueListener> taskEventListenerEntry : map.entrySet()) { String listenerName = taskEventListenerEntry.getValue().getClass().getName(); startThread(listenerName, taskEventListenerEntry.getValue()); } } /** * 啟動(dòng)線程獲取隊(duì)列* * * @param queueName queueName * @param redisDelayedQueueListener 任務(wù)回調(diào)監(jiān)聽(tīng) * @param <T> 泛型 * @return */ private <T> void startThread(String queueName, RedisDelayedQueueListener redisDelayedQueueListener) { RBlockingQueue<T> blockingFairQueue = redissonClient.getBlockingQueue(queueName); //服務(wù)重啟后,無(wú)offer,take不到信息。 redissonClient.getDelayedQueue(blockingFairQueue); //由于此線程需要常駐,可以新建線程,不用交給線程池管理 Thread thread = new Thread(() -> { logger.info("啟動(dòng)監(jiān)聽(tīng)隊(duì)列線程" + queueName); while (true) { try { T t = blockingFairQueue.take(); logger.info("監(jiān)聽(tīng)隊(duì)列線程,監(jiān)聽(tīng)名稱:{},內(nèi)容:{}", queueName, t); redisDelayedQueueListener.invoke(t); } catch (Exception e) { logger.info("監(jiān)聽(tīng)隊(duì)列線程錯(cuò)誤,", e); } } }); thread.setName(queueName); thread.start(); } }
創(chuàng)建延遲任務(wù)
@Autowired RedisDelayedQueue queue; ................. queue.addQueueHours(new SchoolAccountPayStateReqVO(dto.getPayNo()),2, PayQCordListener.class);
到此這篇關(guān)于Redisson延遲隊(duì)列的示例的文章就介紹到這了,更多相關(guān)Redisson延遲隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)入門(mén) Swing中間容器的使用
這篇文章主要介紹了Java基礎(chǔ)入門(mén) Swing中間容器的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12使用spring?data的page和pageable如何實(shí)現(xiàn)分頁(yè)查詢
這篇文章主要介紹了使用spring?data的page和pageable如何實(shí)現(xiàn)分頁(yè)查詢,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12spring基于注解配置實(shí)現(xiàn)事務(wù)控制操作
這篇文章主要介紹了spring基于注解配置實(shí)現(xiàn)事務(wù)控制操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Javamail使用過(guò)程中常見(jiàn)問(wèn)題解決方案
這篇文章主要介紹了Javamail使用過(guò)程中常見(jiàn)問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Struts2攔截器Interceptor的原理與配置實(shí)例詳解
攔截器是一種AOP(面向切面編程)思想的編程方式.它提供一種機(jī)制是開(kāi)發(fā)者能夠把相對(duì)獨(dú)立的代碼抽離出來(lái),配置到Action前后執(zhí)行。下面這篇文章主要給大家介紹了關(guān)于Struts2攔截器Interceptor的原理與配置的相關(guān)資料,需要的朋友可以參考下。2017-11-11Java利用Geotools從DEM數(shù)據(jù)中讀取指定位置的高程信息全過(guò)程
Geotools作為一款功能強(qiáng)大且開(kāi)源的地理工具庫(kù),為地理數(shù)據(jù)的處理和分析提供了豐富的類庫(kù)和便捷的接口,能夠很好地滿足從DEM數(shù)據(jù)中讀取高程信息這一實(shí)戰(zhàn)需求,本文將深入講解如何利用Geotools從獲取DEM數(shù)據(jù)到成功讀取指定位置高程信息的全過(guò)程,需要的朋友可以參考下2025-03-03Spring Boot 使用 Swagger 構(gòu)建 RestAPI 接口文檔
這篇文章主要介紹了Spring Boot 使用 Swagger 構(gòu)建 RestAPI 接口文檔,幫助大家更好的理解和使用Spring Boot框架,感興趣的朋友可以了解下2020-10-10舉例解析Java的設(shè)計(jì)模式編程中里氏替換原則的意義
這篇文章主要介紹了Java的設(shè)計(jì)模式中里氏替換原則的意義,文中舉例來(lái)說(shuō)明里氏替換原則中強(qiáng)調(diào)的繼承特性方面可能帶來(lái)的問(wèn)題,需要的朋友可以參考下2016-02-02