主流的使用主流的消息队列来实现延时消息
延时队列可以通过 Redis 的 zset(有序列表)来实现。我们将消息序列化成一个字符串作为 zset 的value,这个消息的到期处理时间作为 score,然后用多个线程轮询zset 获取到期的任务进行处理。多个线程是为了保障可用性,万一挂了一个线程还有其他线程可以继续处理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不会被多次执行
Redis 的 zrem 方法是多线程多进程争抢任务的关键,它的返回值决定了当前实例有没有抢到任务,因为 loop 方法可能会被多个线程、多个进程调用,同一个任务可能会被多个进程、多个线程抢到,要通过 zrem 来决定唯一的属主。同时,我们要注意一定要对 handle_msg 进行异常捕获,避免因为个别任务处理问题导致循环异常退出。以下是 Java 版本的延时队列实现方法,因为要使用到JSON序列化,所以还需要 fastjson 库的支持。





在上面的算法中,同一个任务可能会被多个进程取到之后再使用 zrem 进行争抢,那些没抢到的进程都白取了一次任务,这是浪费。可以考虑使用 lua scripting 来优化一下这个逻辑,将 zrangebyscore 和 zrem 一同挪到服务器端进行原子化操作,这样多个进程之间争抢任务时就不会出现这种浪费了

