成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用Redisson優雅關閉訂單

開發 前端 Redis
針對于定時任務的這種缺陷,關閉訂單的這個需求大多依賴于延時任務來實現,這里說明一下延時任務與定時任務的最大不同,定時任務有執行周期的,而延時任務在某事件觸發后一段時間內執行,并沒有執行周期。

[[394210]]

在支付系統中,訂單通常是具有時效性的,例如在下單30分鐘后如果還沒有完成支付,那么就要取消訂單,不能再執行后續流程。說到這,可能大家的第一反應是啟動一個定時任務,來輪詢訂單的狀態是否完成了支付,如果超時還沒有完成,那么就去修改訂單的關閉字段。當然,在數據量小的時候這么干沒什么問題,但是如果訂單的數量上來了,那么就會出現讀取數據的瓶頸,畢竟來一次全表掃描還是挺費時的。

針對于定時任務的這種缺陷,關閉訂單的這個需求大多依賴于延時任務來實現,這里說明一下延時任務與定時任務的最大不同,定時任務有執行周期的,而延時任務在某事件觸發后一段時間內執行,并沒有執行周期。

對于延時任務,可能大家對于RabbitMQ的延時隊列會比較熟悉,用起來也是得心應手,但是你是否知道使用Redis也能實現延時任務的功能呢,今天我們就來看看具體應該如何實現。

使用Redis實現的延時隊列,需要借助Redisson的依賴:

  1. <dependency> 
  2.     <groupId>org.redisson</groupId> 
  3.     <artifactId>redisson-spring-boot-starter</artifactId> 
  4.     <version>3.10.7</version> 
  5. </dependency> 

首先實現往延時隊列中添加任務的方法,為了測試時方便,我們把延遲時間設為30秒。

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

添加一個對隊列的監聽方法,通過實現CommandLineRunner接口,使它在springboot啟動時就開始執行:

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

在方法中,單獨啟動了一個線程來進行監聽,如果有任務進入延時隊列,那么取到訂單號后,調用我們OrderService提供的檢測是否訂單過期的服務,如果過期,那么執行關閉訂單的操作。

創建簡單的OrderService用于測試,提供創建訂單,檢測超時,關閉訂單方法:

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

執行請求,看一下結果:

圖片

在訂單創建30秒后,檢測到延時隊列中有任務任務,調用檢測超時方法檢測到訂單沒有完成后,自動關閉訂單。

除了上面這種延時隊列的方式外,Redisson還提供了另一種方式,也能優雅的關閉訂單,方法很簡單,就是通過對將要過期的key值的監聽。

創建一個類繼承KeyExpirationEventMessageListener,重寫其中的onMessage方法,就能實現對過期key的監聽,一旦有緩存過期,就會調用其中的onMessage方法:

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

因為可能會有很多key的過期事件,因此需要對訂單過期的key加上一個前綴,用來判斷過期的key是不是屬于訂單事件,如果是的話那么進行關閉訂單操作。

再在寫一個測試接口,用于創建訂單和接收支付成功的回調結果:

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

在訂單支付成功后,一般我們會收到第三方的一個支付成功的異步回調通知。如果支付完成后收到了這個回調,那么我們主動刪除緩存的未支付訂單,那么也就不會監聽到這個訂單的orderId的過期失效事件。

但是這種方式有一個弊端,就是只能監聽到過期緩存的key,不能獲取到對應的value。而通過延時隊列的方式,可以通過為RBlockingQueue添加泛型的方式,保存更多訂單的信息,例如直接將對象存進隊列中:

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

這樣的話我們再從延時隊列中獲取的時候,能夠拿到更多我們需要的屬性。綜合以上兩種方式,監聽過期更為簡單,但存在的一定的局限性,如果我們只需要對訂單進行判斷的話那么功能也能夠滿足我們的需求,如果需要在過期時獲取更多的訂單屬性,那么使用延時隊列的方式則更為合適。究竟選擇哪種,就要看大家的業務場景了。

 

責任編輯:姜華 來源: 碼農參上
相關推薦

2021-03-28 09:17:18

JVM場景鉤子函數

2020-10-21 09:25:01

互聯網訂單自動關閉

2021-01-19 10:35:49

JVM場景函數

2025-01-13 06:00:00

Go語言gRPC

2017-12-19 10:03:44

JavaLinux代碼

2022-04-11 08:17:07

JVMJava進程

2024-11-13 16:37:00

Java線程池

2024-09-05 09:10:07

2025-06-30 07:10:00

JavaJVM線程

2017-07-26 11:32:50

NETRabbitMQ系統集成

2015-11-26 10:53:45

LinuxWindowsMac OS

2021-12-06 09:57:25

容器Linux信號

2025-02-12 00:21:44

Java并發編程

2023-12-20 10:04:45

線程池Java

2021-08-10 07:41:24

ContextWaitGroupGoroutine

2021-11-17 10:25:28

loguru日志Python

2021-09-07 08:14:26

訂單超時未支付

2023-06-28 08:25:14

事務SQL語句

2023-06-16 09:08:39

ReactContextRFC

2021-11-23 10:45:57

StopWatch代碼Java
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国产综合久久香蕉麻豆 | 国产精品久久毛片av大全日韩 | 欧美日韩亚洲二区 | a免费观看 | 久久九 | 欧美电影在线观看网站 | 亚洲成人高清 | 日本三级日产三级国产三级 | 都市激情亚洲 | 欧美一区二区综合 | 在线成人精品视频 | 国产日韩欧美在线 | 中文字幕亚洲国产 | 一区二区不卡 | 国产精品一区二区三区在线 | 国产一区二区在线视频 | 亚洲综合国产精品 | 亚洲成人在线免费 | 久久久久久毛片免费观看 | 久久精品综合 | 老司机67194精品线观看 | 国产精品99久久久久久久久久久久 | 亚洲一区二区三区免费在线观看 | 欧美精品中文字幕久久二区 | 亚洲视频一区二区三区 | 国产精品久久久久久久久久久免费看 | 麻豆精品一区二区三区在线观看 | 在线视频一区二区 | 日韩视频在线一区 | 国产欧美精品在线 | 乳色吐息在线观看 | 毛片99 | 国产99久久精品一区二区永久免费 | 日韩免费1区二区电影 | 最新国产在线 | 国产精品久久国产精品久久 | 欧美成人在线网站 | 二区在线观看 | 精品国产一区久久 | 在线亚洲免费视频 | 欧美精产国品一二三区 |