用Redis用作消息队列

97 阅读1分钟

异步消息队列

使用lpush/rpush来入队,使用lpop/rpop来出队

队列空了怎么办

  • 使用 sleep 来解决这个问题,让线程睡一会,睡个 1s 钟就可以了
  • 用 blpop/brpop 替代前面的 lpop/rpop,减少延迟的时间

空闲连接自动断开

  • 注意捕获异常

延迟消息队列

主要是通过zset来实现,我们将消息序列为一个字符串作为zset的value,这个消息的到期时间设置为score,然后多个线程轮询zset获取到期的任务进行处理,多个线程是为了保障可用性,但也需要考虑并发问题

生产者

   通过zadd命令来往key里面放消息
public void delay(T msg) { 
    TaskItem task = new TaskItem(); 
    task.id = UUID.randomUUID().toString(); // 分配唯一的 uuid 
    task.msg = msg; 
    String s = JSON.toJSONString(task); // fastjson 序列化
    jedis.zadd(queueKey, System.currentTimeMillis() + 5000, s); // 塞入延时队列 ,5s 后再试
}

消费者

通过zrangeByScore来获取数据

      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 s = values.iterator().next(); 
            if (jedis.zrem(queueKey, s) > 0) { // 抢到了
            TaskItem task = JSON.parseObject(s, TaskType); // fastjson 反序列化
            this.handleMsg(task.msg); 
            } 
            } 
    }
    
Redis 的 zrem 方法是多线程多进程争抢任务的关键,它的返回值决定了当前实例有没有抢到任务,因为 loop 
方法可能会被多个线程、多个进程调用,同一个任务可能会被多个进程线程抢到,通过 zrem来决定唯一的属主。