在開發(fā)中,往往會遇到一些關(guān)于延時任務(wù)的需求浅役。例如
- 生成訂單30分鐘未支付溢谤,則自動取消
- 生成訂單60秒后,給用戶發(fā)短信
對上述的任務(wù)瞻凤,我們給一個專業(yè)的名字來形容,那就是延時任務(wù)世杀。那么這里就會產(chǎn)生一個問題阀参,這個延時任務(wù)和定時任務(wù)的區(qū)別究竟在哪里呢?一共有如下幾點區(qū)別:
定時任務(wù)有明確的觸發(fā)時間瞻坝,延時任務(wù)沒有
定時任務(wù)有執(zhí)行周期蛛壳,而延時任務(wù)在某事件觸發(fā)后一段時間內(nèi)執(zhí)行杏瞻,沒有執(zhí)行周期
定時任務(wù)一般執(zhí)行的是批處理操作是多個任務(wù),而延時任務(wù)一般是單個任務(wù)
解決方案:
- 1衙荐、JDK的延遲隊列
- 2捞挥、時間輪算法--HashedWheelTimer
- 3、Redisson延遲隊列RDelayedQueue
前倆方案的優(yōu)缺點:
- 優(yōu)點:
效率高,任務(wù)觸發(fā)時間延遲時間比delayQueue低忧吟,代碼復雜度比delayQueue低砌函。 - 缺點:
(1)服務(wù)器重啟后,數(shù)據(jù)全部消失溜族,怕宕機 ??
(2)集群擴展相當麻煩 ??
(3)因為內(nèi)存條件限制的原因讹俊,比如下單未付款的訂單數(shù)太多,那么很容易就出現(xiàn)OOM異常
使用Redisson延遲隊列RDelayedQueue
1煌抒、 pom.xml
<!-- JDK 1.8+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.2</version>
</dependency>
<!-- JDK 1.6+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.10.4</version>
</dependency>
2仍劈、隊列中要存入的元素實體
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employer {
private String name;
private int age;
private String wife;
private Double salary;
private String putTime;
public void setPutTime() {
this.putTime = new SimpleDateFormat("hh:mm:ss").format(new Date());
}
}
3、生成訂單并放進延時隊列的類
package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedisPutInQueue {
public static void main(String args[]) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RBlockingQueue<Employer> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue");
RDelayedQueue<Employer> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
for (int i = 0; i < 10; i++) {
try {
//模擬間隔投遞消息
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 一分鐘以后將消息發(fā)送到指定隊列
//相當于1分鐘后取消訂單
//延遲隊列包含callCdr 1分鐘寡壮,然后將其傳輸?shù)絙lockingFairQueue中
//在1分鐘后就可以在blockingFairQueue 中獲取callCdr了
Employer callCdr = new Employer();
callCdr.setSalary(345.6);
callCdr.setPutTime();
delayedQueue.offer(callCdr, 1, TimeUnit.MINUTES);
System.out.println("callCdr =================================> " + callCdr);
}
//在該對象不再需要的情況下贩疙,應該主動銷毀。
// 僅在相關(guān)的Redisson對象也需要關(guān)閉的時候可以不用主動銷毀况既。
delayedQueue.destroy();
//redissonClient.shutdown();
}
}
4这溅、取消訂單的操作類
package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RedisOutFromQueue {
public static void main(String args[]) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RBlockingQueue<Employer> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue");
RDelayedQueue<Employer> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
while (true) {
Employer callCdr = null;
try {
callCdr = blockingFairQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("訂單取消時間:" + new SimpleDateFormat("hh:mm:ss").format(new Date()) + "==訂單生成時間" + callCdr.getPutTime());
}
}
}
參考: