Kafka 延时队列&重试队列

Kafka延时队列与重试机制解析
本文详细介绍了Kafka的延时队列实现,包括时间轮、延迟生产和延迟拉取的原理及流程。延时队列通过尝试完成和超时完成两种方式确保操作的正确执行。此外,文章指出Kafka本身不支持内置的重试机制,用户需要自行实现,通常涉及创建重试主题、存储和定时消费重试消息等步骤。

一、延时队列

1. 简介

TimingWheel是kafka时间轮的实现,内部包含了⼀个TimerTaskList数组,每个数组包含了⼀些链表组成的TimerTaskEntry事件,每个TimerTaskList表示时间轮的某⼀格,这⼀格的时间跨度为tickMs,同⼀个TimerTaskList中的事件都是相差在⼀个tickMs跨度内的,整个时间轮的时间跨度为interval = tickMs * wheelSize,该时间轮能处理的时间范围在cuurentTime到currentTime + interval之间的事件。

当添加⼀个时间他的超时时间⼤于整个时间轮的跨度时, expiration >= currentTime + interval,则会将该事件向上级传递,上级的tickMs是下级的interval,传递直到某⼀个时间轮满⾜expiration < currentTime + interval,

然后计算对应位于哪⼀格,然后将事件放进去,重新设置超时时间,然后放进jdk延迟队列

else if (expiration < currentTime + interval) {
    // Put in its own bucket
    val virtualId = expiration / tickMs 
    val bucket = buckets((virtualId % wheelSize.toLong).toInt) 
    bucket.add(timerTaskEntry)
    // Set the bucket expiration time
    if (bucket.setExpiration(virtualId * tickMs)) {
        // The bucket needs to be enqueued because it was an expired bucket
        // We only need to enqueue the bucket when its expiration time has changed, i.e. the wheel has advanced
        // and the previous buckets gets reused; further calls to set the expiration within the same wheel cycle
        // will pass in the same value and hence return false, thus the bucket with the same expiration will not
        // be enqueued multiple times.
        queue.offer(bucket)
    }

SystemTimer会取出queue中的TimerTaskList,根据expiration将currentTime往前推进,然后把⾥⾯所有的事件重新放进时间轮中,因为ct推进了,所以有些事件会在第0格,表示到期了,直接返回。

else if (expiration < currentTime + tickMs) {

然后将任务提交到java线程池中处理。

服务端在处理客户端的请求,针对不同的请求,可能不会⽴即返回响应结果给客户端。在处理这类请求时,服务端会为这类请求创建延迟操作对象放⼊延迟缓存队列中。延迟缓存的数据结构类似MAP,延迟操作对象从延迟缓存队列中完成并移除有两种⽅式:

  1. 延迟操作对应的外部事件发⽣时,外部事件会尝试完成延迟缓存中的延迟操作 。
  2. 如果外部事件仍然没有完成延迟操作,超时时间达到后,会强制完成延迟的操作 。

2. 延时操作接口

​ ​DelayedOperation​ ​接⼝表示延迟的操作对象。此接⼝的实现类包括延迟加⼊,延迟⼼跳,延迟⽣产,延迟拉取。延迟接⼝相关的⽅法:

  • ​ ​tryComplete​ ​​:尝试完成,外部事件发⽣时会尝试完成延迟的操作。该⽅法返回值为true,表示可以完成延迟操作,会调⽤强制完成的⽅法(​ ​forceComplete​ ​)。返回值为false,表示不可以完成延迟操作。
  • ​ ​forceComplete​ ​​:强制完成,两个地⽅调⽤,尝试完成⽅法(​ ​tryComplete​
Apache Kafka 本身并不是为实现**延时队列**(Delayed Queue)而设计的消息队列系统,它更擅长于高吞吐量的实时数据流处理。但通过一些技巧和扩展,我们可以在 Kafka 上实现**延时队列**的功能。 --- ### 一、Kafka 实现延时队列的常见方式 #### 1. **基于时间戳 + 消费者轮询** Kafka 支持消息的时间戳(Log Append Time 或 Create Time),消费者可以根据消息的时间戳判断是否满足延时条件。 **实现思路:** - 生产者发送消息时设置一个&ldquo;期望消费时间&rdquo;。 - 消费者不立即处理消息,而是先将消息暂存到本地缓存或内存队列。 - 消费者定时检查缓存中是否有消息达到消费时间。 ```java // 示例伪代码 while (true) { ConsumerRecords&lt;String, String&gt; records = consumer.poll(Duration.ofMillis(100)); for (ConsumerRecord&lt;String, String&gt; record : records) { long expectedTime = parseExpectedTime(record.value()); if (System.currentTimeMillis() &gt;= expectedTime) { process(record); } else { delayQueue.offer(new DelayMessage(record, expectedTime)); } } // 定时检查 delayQueue 中的消息是否到期 checkAndProcess(delayQueue); } ``` ✅ **优点**:实现简单,适用于轻量级延时需求 ❌ **缺点**:不适用于高精度、大规模延时场景 --- #### 2. **分层队列 + 定时调度** 将延时消息按时间分层,比如每 1 分钟一个 Kafka 分区,消费者定时消费对应分区。 例如: - 延时 1~60 秒的消息写入 `delay-1m` topic - 延时 1~5 分钟的消息写入 `delay-5m` topic - ... 消费者定时拉取对应 topic 的消息进行处理。 ✅ **优点**:可控制粒度,适合中等规模延时队列 ❌ **缺点**:管理多个 topic,逻辑较复杂 --- #### 3. **结合 Kafka + Redis / RocksDB / LevelDB** 将消息写入 Kafka,同时将消息 ID 和延时时间存入 Redis 的 Sorted Set。 消费者从 Redis 中定时拉取已到期的消息 ID,再根据 ID 到 Kafka 中查找处理。 ✅ **优点**:可精确控制延时时间,支持高并发 ❌ **缺点**:引入额外组件,系统复杂度上升 --- #### 4. **使用 Kafka Streams + 状态存储** 使用 Kafka Streams API,结合状态存储(如 RocksDB),将消息缓存一段时间后触发处理。 ```java KStream&lt;byte[], byte[]&gt; delayedStream = builder.stream(&quot;input-topic&quot;); delayedStream .transformValues(() -&gt; new DelayedTransformer()) .to(&quot;output-topic&quot;); ``` ✅ **优点**:完全运行在 Kafka 生态中 ❌ **缺点**:开发复杂度较高,适合有 Kafka Streams 经验的团队 --- #### 5. **使用 Kafka + 时间调度服务(如 Quartz)** 将消息写入 Kafka,同时记录到数据库或调度系统中,由 Quartz 等定时任务触发消费。 ✅ **优点**:功能强大,适合复杂业务 ❌ **缺点**:系统依赖多,维护成本高 --- ### 二、Kafka 延时队列适用场景 | 场景 | 描述 | |------|------| | 订单超时关闭 | 用户下单后 N 分钟未支付,自动取消订单 | | 优惠券发放 | 用户注册后 N 天发送优惠券 | | 消息重试 | 消息失败后延迟重试 | | 定时通知 | 用户下单后 N 小时发送提醒通知 | --- ### 三、Kafka 延时队列 vs 其他消息中间件 | 特性 | Kafka | RabbitMQ(插件) | ActiveMQ | |------|-------|------------------|----------| | 原生支持延时队列 | ❌ | ✅(插件) | ✅ | | 吞吐量 | 高 | 中 | 中 | | 实现复杂度 | 高 | 低 | 低 | | 适合场景 | 大数据流、日志处理 | 精准延时任务 | Java 企业级应用 | --- ### 四、总结 虽然 Kafka 本身不支持延时队列,但通过合理设计可以实现类似功能。选择实现方式时应根据业务需求权衡: - **简单场景**:使用消费者本地缓存 + 时间戳判断 - **中等复杂度**:使用分层队列 + 定时消费 - **高精度需求**:结合 Redis、Kafka Streams 或 Quartz ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值