Kafka 进阶学习(十九)—— 简单实现延时队列

838 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

前言

今天是我 Kafka 学习的第 19 天,今天学习的内容是 Kafka 的延时队列实现。原生 Kafka 中是不支持延时队列功能的,我们在使用其他消息中间件时,比如RabbitMQ,使用到了延时队列的功能,如果我们将应用直接切换到 Kafka 中,那么只能选择舍弃它们。

其实还有另外一种方式,就是我们来扩展一下 Kafka,让他来支持延时队列这个功能,下面我们来看看实现思路。

延时队列

在介绍延时队列实现方式之间,我们先简单看下延时队列概念和应用场景。

队列是存储消息的载体,延时队列存储的对象是延时消息。所谓的“延时消息”是指消息被发送以后,并不想让消费者立刻获取,而是等待特定的时间后,消费者才能获取这个消息进行消费,延时队列一般也被称为“延迟队列”。

注意延时与 TTL 是有区别的,延时的消息达到目标延时时间后才能被消费,而 TTL 的消息达到目标超时时间后会被丢弃。

延时队列的使用场景有很多,比如:

  1. 在订单系统中,一个用户下单之后通常有 30 分钟的时间进行支付,如果 30 分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延时队列来处理这些订单了。
  2. 订单完成 1 时后通知用户进行评价。
  3. 用户希望通过手机远程遥控家里的智能设备在指定时间进行工作。这时就可以将用户指令发送到延时队列,当指令设定的时间到了之后再将它推送到智能设备。

延时队列设计思路

我们在发送延时消息的时候并不是先投递到要发送的真实主题(real_topic)中,而是先投递到一些 Kafka 内部的主题(delay_topic)中,这些内部主题对用户不可见,然后通过一个自定义的服务拉取这些内部主题中的消息,并将满足条件的消息再投递到要发送的真实的主题中,消费者所订阅的还是真实的主题。

这样我们就可以实现延时一个延时队列的功能。

延时时间一般以秒来计,假设我们要支持 2 小时(7200 秒)之内的延时时间的消息,那我们如何来划分内部主体呢。

显然我们不能按照延时时间来分类这些内部主题。试想一个集群中需要额外的 7200 个主题,每个主题再分成多个分区,每个分区又有多个副本,每个副本又可以分多个日志段,每个日志段中也包含多个文件,这样不仅会造成资源的极度浪费,也会造成系统吞吐的大幅下降。

那我们再扩大下时间范围,采用不同的延时等级来划分,比如设定 5s、10s、30s、1min、2min、5min、10min、20min、30min、45min、1hour、2hour 这些按延时时间递增的延时等级,延时的消息按照延时时间投递到不同等级的主题中,投递到同一主题中的消息的延时时间会被强转为与此主题延时等级一致的延时时间,这样延时误差控制在两个延时等级的时间差范围之内(比如延时时间为 17s 的消息投递到 30s 的延时主题中,之后按照延时时间为 30s 进行计算,延时误差为 13s)。虽然有一定的延时误差,但是误差可控,并且这样只需增加少许的主题就能实现延时队列的功能。

参考文档

  • 《深入理解 Kafka:核心设计与实践原理》—— 朱忠华

往期文章