日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
使用Redisson優(yōu)雅關(guān)閉訂單

在支付系統(tǒng)中,訂單通常是具有時(shí)效性的,例如在下單30分鐘后如果還沒(méi)有完成支付,那么就要取消訂單,不能再執(zhí)行后續(xù)流程。說(shuō)到這,可能大家的第一反應(yīng)是啟動(dòng)一個(gè)定時(shí)任務(wù),來(lái)輪詢訂單的狀態(tài)是否完成了支付,如果超時(shí)還沒(méi)有完成,那么就去修改訂單的關(guān)閉字段。當(dāng)然,在數(shù)據(jù)量小的時(shí)候這么干沒(méi)什么問(wèn)題,但是如果訂單的數(shù)量上來(lái)了,那么就會(huì)出現(xiàn)讀取數(shù)據(jù)的瓶頸,畢竟來(lái)一次全表掃描還是挺費(fèi)時(shí)的。

針對(duì)于定時(shí)任務(wù)的這種缺陷,關(guān)閉訂單的這個(gè)需求大多依賴于延時(shí)任務(wù)來(lái)實(shí)現(xiàn),這里說(shuō)明一下延時(shí)任務(wù)與定時(shí)任務(wù)的最大不同,定時(shí)任務(wù)有執(zhí)行周期的,而延時(shí)任務(wù)在某事件觸發(fā)后一段時(shí)間內(nèi)執(zhí)行,并沒(méi)有執(zhí)行周期。

對(duì)于延時(shí)任務(wù),可能大家對(duì)于RabbitMQ的延時(shí)隊(duì)列會(huì)比較熟悉,用起來(lái)也是得心應(yīng)手,但是你是否知道使用Redis也能實(shí)現(xiàn)延時(shí)任務(wù)的功能呢,今天我們就來(lái)看看具體應(yīng)該如何實(shí)現(xiàn)。

使用Redis實(shí)現(xiàn)的延時(shí)隊(duì)列,需要借助Redisson的依賴:

 
 
 
 
  1.     org.redisson
  2.     redisson-spring-boot-starter
  3.     3.10.7

首先實(shí)現(xiàn)往延時(shí)隊(duì)列中添加任務(wù)的方法,為了測(cè)試時(shí)方便,我們把延遲時(shí)間設(shè)為30秒。

 
 
 
 
  1. @Component
  2. public class UnpaidOrderQueue {
  3.     @Autowired
  4.     RedissonClient redissonClient;
  5.     public void addUnpaid(String orderId){
  6.         RBlockingQueue blockingFairQueue = redissonClient.getBlockingQueue("orderQueue");
  7.         RDelayedQueue delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
  8.         System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+" 添加任務(wù)到延時(shí)隊(duì)列");
  9.         delayedQueue.offer(orderId,30, TimeUnit.SECONDS);
  10.     }
  11. }

添加一個(gè)對(duì)隊(duì)列的監(jiān)聽(tīng)方法,通過(guò)實(shí)現(xiàn)CommandLineRunner接口,使它在springboot啟動(dòng)時(shí)就開(kāi)始執(zhí)行:

 
 
 
 
  1. @Component
  2. public class QueueRunner implements CommandLineRunner {
  3.     @Autowired
  4.     private RedissonClient redissonClient;
  5.     @Autowired
  6.     private OrderService orderService;
  7.     @Override
  8.     public void run(String... args) throws Exception {
  9.         new Thread(()->{
  10.             RBlockingQueue blockingFairQueue = redissonClient.getBlockingQueue("orderQueue");
  11.             RDelayedQueue delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
  12.             delayedQueue.offer(null, 1, TimeUnit.SECONDS);
  13.             while (true){
  14.                 String orderId = null;
  15.                 try {
  16.                     orderId = blockingFairQueue.take();
  17.                 } catch (Exception e) {
  18.                     continue;
  19.                 }
  20.                 if (orderId==null) {
  21.                     continue;
  22.                 }
  23.                 System.out.println(String.format(DateTime.now().toString(JodaUtil.HH_MM_SS)+" 延時(shí)隊(duì)列收到:"+orderId));
  24.                 System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+" 檢測(cè)訂單是否完成支付");
  25.                 if (orderService.isTimeOut(orderId)) {
  26.                     orderService.closeOrder(orderId);
  27.                 }
  28.             }
  29.         }).start();
  30.     }
  31. }

在方法中,單獨(dú)啟動(dòng)了一個(gè)線程來(lái)進(jìn)行監(jiān)聽(tīng),如果有任務(wù)進(jìn)入延時(shí)隊(duì)列,那么取到訂單號(hào)后,調(diào)用我們OrderService提供的檢測(cè)是否訂單過(guò)期的服務(wù),如果過(guò)期,那么執(zhí)行關(guān)閉訂單的操作。

創(chuàng)建簡(jiǎn)單的OrderService用于測(cè)試,提供創(chuàng)建訂單,檢測(cè)超時(shí),關(guān)閉訂單方法:

 
 
 
 
  1. @Service
  2. public class OrderService {
  3.     @Autowired
  4.     UnpaidOrderQueue unpaidOrderQueue;
  5.     public void createOrder(String order){
  6.         System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+" 創(chuàng)建訂單:"+order);
  7.         unpaidOrderQueue.addUnpaid(order);
  8.     }
  9.     public boolean isTimeOut(String orderId){
  10.         return true;
  11.     }
  12.     public void closeOrder(String orderId){
  13.         System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+ " 關(guān)閉訂單");
  14.     }
  15. }

執(zhí)行請(qǐng)求,看一下結(jié)果:

在訂單創(chuàng)建30秒后,檢測(cè)到延時(shí)隊(duì)列中有任務(wù)任務(wù),調(diào)用檢測(cè)超時(shí)方法檢測(cè)到訂單沒(méi)有完成后,自動(dòng)關(guān)閉訂單。

除了上面這種延時(shí)隊(duì)列的方式外,Redisson還提供了另一種方式,也能優(yōu)雅的關(guān)閉訂單,方法很簡(jiǎn)單,就是通過(guò)對(duì)將要過(guò)期的key值的監(jiān)聽(tīng)。

創(chuàng)建一個(gè)類繼承KeyExpirationEventMessageListener,重寫(xiě)其中的onMessage方法,就能實(shí)現(xiàn)對(duì)過(guò)期key的監(jiān)聽(tīng),一旦有緩存過(guò)期,就會(huì)調(diào)用其中的onMessage方法:

 
 
 
 
  1. @Component
  2. public class RedisExpiredListener extends KeyExpirationEventMessageListener {
  3.     public static final String UNPAID_PREFIX="unpaidOrder:";
  4.     @Autowired
  5.     OrderService orderService;
  6.     public RedisExpiredListener(RedisMessageListenerContainer listenerContainer) {
  7.         super(listenerContainer);
  8.     }
  9.     @Override
  10.     public void onMessage(Message message, byte[] pattern) {
  11.         String expiredKey = message.toString();
  12.         if (expiredKey.startsWith(UNPAID_PREFIX)){
  13.             System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+" " +expiredKey+"已過(guò)期");
  14.             orderService.closeOrder(expiredKey);
  15.         }
  16.     }
  17. }

因?yàn)榭赡軙?huì)有很多key的過(guò)期事件,因此需要對(duì)訂單過(guò)期的key加上一個(gè)前綴,用來(lái)判斷過(guò)期的key是不是屬于訂單事件,如果是的話那么進(jìn)行關(guān)閉訂單操作。

再在寫(xiě)一個(gè)測(cè)試接口,用于創(chuàng)建訂單和接收支付成功的回調(diào)結(jié)果:

 
 
 
 
  1. @RestController
  2. @RequestMapping("order")
  3. public class TestController {
  4.     @Autowired
  5.     RedisTemplate redisTemplate;
  6.     @GetMapping("create")
  7.     public String setTemp(String id){
  8.         String orderId= RedisExpiredListener.UNPAID_PREFIX+id;
  9.         System.out.println(DateTime.now().toString(JodaUtil.HH_MM_SS)+" 創(chuàng)建訂單:"+orderId);
  10.         redisTemplate.opsForValue().set(orderId,orderId,30, TimeUnit.SECONDS);
  11.         return id;
  12.     }
  13.     @GetMapping("fallback")
  14.     public void successFallback(String id){
  15.         String orderId= RedisExpiredListener.UNPAID_PREFIX+id;
  16.         redisTemplate.delete(orderId);
  17.     }
  18. }

在訂單支付成功后,一般我們會(huì)收到第三方的一個(gè)支付成功的異步回調(diào)通知。如果支付完成后收到了這個(gè)回調(diào),那么我們主動(dòng)刪除緩存的未支付訂單,那么也就不會(huì)監(jiān)聽(tīng)到這個(gè)訂單的orderId的過(guò)期失效事件。

但是這種方式有一個(gè)弊端,就是只能監(jiān)聽(tīng)到過(guò)期緩存的key,不能獲取到對(duì)應(yīng)的value。而通過(guò)延時(shí)隊(duì)列的方式,可以通過(guò)為RBlockingQueue添加泛型的方式,保存更多訂單的信息,例如直接將對(duì)象存進(jìn)隊(duì)列中:

 
 
 
 
  1. RBlockingQueue blockingFairQueue = redissonClient.getBlockingQueue("orderQueue");
  2. RDelayedQueue delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);

這樣的話我們?cè)購(gòu)难訒r(shí)隊(duì)列中獲取的時(shí)候,能夠拿到更多我們需要的屬性。綜合以上兩種方式,監(jiān)聽(tīng)過(guò)期更為簡(jiǎn)單,但存在的一定的局限性,如果我們只需要對(duì)訂單進(jìn)行判斷的話那么功能也能夠滿足我們的需求,如果需要在過(guò)期時(shí)獲取更多的訂單屬性,那么使用延時(shí)隊(duì)列的方式則更為合適。究竟選擇哪種,就要看大家的業(yè)務(wù)場(chǎng)景了。


網(wǎng)頁(yè)標(biāo)題:使用Redisson優(yōu)雅關(guān)閉訂單
網(wǎng)址分享:http://www.5511xx.com/article/coedddo.html