Redis实现异步消息队列,延时队列

异步消息队列

        Redis中的 list(列表)实现异步消息队列,使用rpush / lpush 操作插入队列消息,使用 lpop 和  rpop 来出队消息。

        

队列空了怎么办?

       如果队列空了,客户端就会陷入pop的死循环,不停地pop,没有数据,接着pop,有没有数据。这样的空轮询拉高了客户端的CPU,redis的QPS也会被拉高,Redis的慢查询可能会显著增多。

       解决方案:使用命令 blpop、brpop,b(blocking,阻塞)。

       阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立即醒来,消费的延迟几乎为零。用这两个命令代替 lpop、rpop可以解决上面的问题。

延迟队列的实现

       延迟队列可以使用 zset(有序列表)实现,我们将消息序列化成一个字符串作为列表的value,这个消息的到期处理时间作为score,然后多个线程轮询zset 获取到期的任务进行执行,多线程保证了可靠性,因为多个线程,需要考虑并发执行的问题,一个任务不能被多次执行。

代码如下:

package list;

 

import java.lang.reflect.Type;

import java.util.Set;

import java.util.UUID;

 

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.TypeReference;

 

import redis.clients.jedis.Jedis;

 

/**

 * 延时异步消息队列的实现

 */

public class RedisDelayingQueue<T> {

 

    static class TaskItem<T> {

        public String id;

        public T msg;

    }

 

    // fastjson 序列化对象中存在 generic 类型时,需要使用 TypeReference

    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<T> task = new TaskItem<T>();

        task.id = UUID.randomUUID().toString(); // 分配唯一的 uuid

        task.msg = msg;

        String s = JSON.toJSONString(task); // fastjson 序列化

        jedis.zadd(queueKey, System.currentTimeMillis() + 5000, s); // 塞入延时队列 ,5s 后再试

    }

 

    public void loop() {

        while (!Thread.interrupted()) {

            // 只取一条

            Set<String> values = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1);

            if (values.isEmpty()) {

                try {

                    Thread.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); // fastjson 反序列化

                this.handleMsg(task.msg);

            }

        }

    }

 

    public void handleMsg(T msg) {

        System.out.println(msg);

    }

 

    public static void main(String[] args) {

        Jedis jedis = new Jedis();

        final RedisDelayingQueue<String> queue = new RedisDelayingQueue<>(jedis, "q-demo");

        Thread producer = new Thread() {

 

            public void run() {

                for (int i = 0; i < 10; i++) {

                    queue.delay("codehole" + i);

                }

            }

        };

        Thread consumer = new Thread() {

            public void run() {

                queue.loop();

            }

        };

        producer.start();

        consumer.start();

        try {

            producer.join();

            Thread.sleep(6000);

            consumer.interrupt();

            consumer.join();

        } catch (InterruptedException e) {

        }

    }

}

 

 

优化代码如下:待续

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值