Redis實現一個簡單的延時隊列

  1. 延時隊列
    1. 通過redis中的zset我們可以實現一個簡單的延時隊列。通過將score設置爲時間,然後多線程輪訓的去查詢是否有到期的可以執行的任務。
    2. 考慮到多線程併發,使用zrem去保證每一個任務只會被執行1次,這裏也可以使用lua腳本來優化,減少空取的情況。
  2. 代碼實現
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;

/**
 * @author liuxin
 * @version 1.0
 * @date 2020/4/21 20:06
 */
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();
        }

    }


}

  1. 運行結果 可以看到每個任務只被消費了一次。
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
結束
結束
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章