(三)redis实现延时队列

说明:

  • 平时使用rabbitMQ和kafka作为消息队列中间件,但是使用稍微复杂。就rabbitMQ为例,使用前需要创建exchange,queue以及路由routing-key进行绑定
  • redis可以轻松搞定只有一组消息的队列,但是没有非常多的高级特性以及ack保证 

redis的list常用来做异步消息队列使用,使用rpush/lpush操作入队列,使用lpop/rpop来出队列

【问题一】队列空了咋整?

办法:如果队列为空pop会陷入死循环浪费CPU资源,拉高redis的QPS造成redis的慢查询显著增多。通过使用Thread.sleep(1000)睡眠一秒。

【问题二】睡眠一秒会造成延迟咋整?

办法:使用阻塞读blpop/brpop代替之前的lpop/rpop,完美解决

【问题三】空闲连接自动断开咋整?

办法:捕获异常,重试

延时队列的实现

可通过zset,将消息序列化成字符串作为zset的value,消息的到期处理时间作为score。然后使用多个线程轮训zset获取到期的任务继续处理。

本人亲测可直接运行,代码是《Redis深度历险》P28内容


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;

public class RedisDelayingQueue<T> {
    static class TaskItem<T>{
        public String id;
        public T msg;
    }


    private Type TaskType = new TypeReference<TaskItem<T>>(){}.getType();
    private Jedis jedis;
    private String queueKey;

    public RedisDelayingQueue(Jedis jedis,String queueKey){
        this.jedis = jedis;
        this.queueKey = queueKey;
    }

    public void delay(T msg){
        TaskItem task = new TaskItem();
        task.id = UUID.randomUUID().toString(); //分配谓一值uuid
        task.msg = msg;
        String JSONResultString = JSON.toJSONString(task); //fastjson序列化
        jedis.zadd(queueKey,System.currentTimeMillis()+5000,JSONResultString);  //放入延时队列,5s后再试
    }

    public void loop(){
        while (!Thread.interrupted()){
            //只获取一条
            Set values = jedis.zrangeByScore(queueKey,0,System.currentTimeMillis(),0,1);
            if(values.isEmpty()){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    break;
                }
                continue;
            }
            String valuesString = (String) values.iterator().next();
            if(jedis.zrem(queueKey,valuesString) > 0){ //  抢到了
                TaskItem task = JSON.parseObject(valuesString,TaskType);
                this.handleMsg(task.msg);
            }
        }
    }

    private void handleMsg(Object msg) {
        System.out.println(msg);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis();
        RedisDelayingQueue queue = new RedisDelayingQueue<>(jedis,"q-demo");
        Thread producer = new Thread(){
            @Override
            public void run() {
                for(int i = 0 ; i <10 ; i++){
                    queue.delay("codehole" + i);
                }
            }
        };

        Thread consumer = new Thread(){
            @Override
            public void run() {
                queue.loop();
            }
        };

        producer.start();
        consumer.start();

        try {
            producer.join();
            Thread.sleep(6000);
            consumer.interrupt();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

猜你喜欢

转载自blog.csdn.net/lss446937072/article/details/109630913