- 延時隊列
- 通過redis中的zset我們可以實現一個簡單的延時隊列。通過將score設置爲時間,然後多線程輪訓的去查詢是否有到期的可以執行的任務。
- 考慮到多線程併發,使用zrem去保證每一個任務只會被執行1次,這裏也可以使用lua腳本來優化,減少空取的情況。
- 代碼實現
package com.xliu.chapter1;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RedisDelayingQueue<T> {
private Jedis jedis;
private String queueKey;
static class TaskItem<T> {
public String id;
public T msg;
}
private Type TaskType = new TypeReference<TaskItem<T>>(){}.getType();
public RedisDelayingQueue(Jedis jedis, String queueKey) {
this.jedis = jedis;
this.queueKey = queueKey;
}
public void delay(T msg){
TaskItem<T> task = new TaskItem<>();
task.id = UUID.randomUUID().toString();
task.msg = msg;
String s = JSON.toJSONString(task);
long score = System.currentTimeMillis() + 5000;
jedis.zadd(queueKey,score,s);
}
public void loop(){
while(!Thread.interrupted()){
Set<String> values = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1);
if(values.isEmpty()){
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
break;
}
continue;
}
String s = values.iterator().next();
if(jedis.zrem(queueKey,s) > 0){
TaskItem<T> task = JSON.parseObject(s,TaskType);
this.handleMsg((T) (Thread.currentThread().getName() + " " +task.msg));
}
}
System.out.println("結束");
}
private void handleMsg(T msg) {
System.out.println(msg);
}
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.198.128");
Jedis jedis1 = new Jedis("192.168.198.128");
Jedis jedis2 = new Jedis("192.168.198.128");
RedisDelayingQueue<String>queue = new RedisDelayingQueue<>(jedis,"q-demo");
RedisDelayingQueue<String>queue1 = new RedisDelayingQueue<>(jedis1,"q-demo");
RedisDelayingQueue<String>queue2 = new RedisDelayingQueue<>(jedis2,"q-demo");
Thread producer = new Thread(){
@Override
public void run() {
for(int i=0;i<10;i++){
queue.delay("codehole" + i);
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread consumer1 = new Thread(){
@Override
public void run() {
queue1.loop();
}
};
Thread consumer2 = new Thread(){
@Override
public void run() {
queue2.loop();
}
};
producer.start();
consumer1.start();
consumer2.start();
try {
producer.join();
TimeUnit.SECONDS.sleep(10);
consumer1.interrupt();
consumer1.join();
consumer2.interrupt();
consumer2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 運行結果 可以看到每個任務只被消費了一次。
Thread-1 codehole0
Thread-2 codehole1
Thread-1 codehole2
Thread-2 codehole3
Thread-2 codehole4
Thread-2 codehole5
Thread-1 codehole6
Thread-1 codehole7
Thread-1 codehole8
Thread-2 codehole9
結束
結束