消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:38.531 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 1/3): [消费失败] 模拟业务异常
2025-10-15 11:35:38.533 INFO 25208 --- [nPool-worker-18] c.t.s.e.port.kafka.KafkaEventCenter : level=1, expirationTime=1760499339533
2025-10-15 11:35:38.537 INFO 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 1000ms 后重试
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:38.537 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 2/3): [消费失败] 模拟业务异常
2025-10-15 11:35:38.537 INFO 25208 --- [nPool-worker-18] c.t.s.e.port.kafka.KafkaEventCenter : level=2, expirationTime=1760499343537
2025-10-15 11:35:38.540 INFO 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 5000ms 后重试
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:38.540 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 3/3): [消费失败] 模拟业务异常
2025-10-15 11:35:38.541 ERROR 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event failed after 3 retries, sent to DLQ
2025-10-15 11:35:38.544 INFO 25208 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_2
2025-10-15 11:35:38.544 INFO 25208 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_1
2025-10-15 11:35:38.544 INFO 25208 --- [nPool-worker-18] c.t.s.e.port.kafka.KafkaEventCenter : 暂停成功,topic: delay_topic_level_2
2025-10-15 11:35:38.544 INFO 25208 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 暂停成功,topic: delay_topic_level_1
2025-10-15 11:35:38.544 INFO 25208 --- [nPool-worker-18] c.t.s.e.port.kafka.delay.DelayHandler : 暂停目标topic:delay_topic_level_2
2025-10-15 11:35:38.544 INFO 25208 --- [nPool-worker-22] c.t.s.e.port.kafka.delay.DelayHandler : 暂停目标topic:delay_topic_level_1
2025-10-15 11:35:38.544 INFO 25208 --- [ad | producer-1] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 发送至死信队列!原始事件: {"filterKey":"key","timeStamp":1760499338540,"message":"测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信","retryCount":3},时间戳:1760499338541,主题: hello-unicast-dlq-no-partition_dlq_topic, 分区: 0, 已重试次数: 3,最后一次异常信息:[消费失败] 模拟业务异常
[死信捕获] 死信Topic: hello-unicast-dlq-no-partition_dlq_topic, 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:39.550 INFO 25208 --- [pool-2-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_1, currentTime=1760499339550
2025-10-15 11:35:40.052 INFO 25208 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_1
2025-10-15 11:35:40.052 INFO 25208 --- [pool-2-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 恢复成功,topic: delay_topic_level_1
2025-10-15 11:35:40.052 INFO 25208 --- [pool-2-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 恢复目标topic消费:delay_topic_level_1
2025-10-15 11:35:40.053 INFO 25208 --- [pool-2-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: hello-unicast-dlq-no-partition
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:40.072 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 2/3): [消费失败] 模拟业务异常
2025-10-15 11:35:40.072 INFO 25208 --- [nPool-worker-18] c.t.s.e.port.kafka.KafkaEventCenter : level=2, expirationTime=1760499345072
2025-10-15 11:35:40.073 INFO 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 5000ms 后重试
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:40.073 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 3/3): [消费失败] 模拟业务异常
2025-10-15 11:35:40.073 ERROR 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event failed after 3 retries, sent to DLQ
2025-10-15 11:35:40.076 INFO 25208 --- [ad | producer-1] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 发送至死信队列!原始事件: {"filterKey":"key","timeStamp":1760499340073,"message":"测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信","retryCount":3},时间戳:1760499340073,主题: hello-unicast-dlq-no-partition_dlq_topic, 分区: 0, 已重试次数: 3,最后一次异常信息:[消费失败] 模拟业务异常
[死信捕获] 死信Topic: hello-unicast-dlq-no-partition_dlq_topic, 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:43.550 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760499343550
2025-10-15 11:35:43.627 INFO 25208 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_2
2025-10-15 11:35:43.627 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 恢复成功,topic: delay_topic_level_2
2025-10-15 11:35:43.627 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 恢复目标topic消费:delay_topic_level_2
2025-10-15 11:35:43.630 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: hello-unicast-dlq-no-partition
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:43.635 WARN 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 3/3): [消费失败] 模拟业务异常
2025-10-15 11:35:43.635 ERROR 25208 --- [nPool-worker-18] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event failed after 3 retries, sent to DLQ
2025-10-15 11:35:43.636 INFO 25208 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_2
2025-10-15 11:35:43.636 INFO 25208 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 暂停成功,topic: delay_topic_level_2
2025-10-15 11:35:43.636 INFO 25208 --- [nPool-worker-22] c.t.s.e.port.kafka.delay.DelayHandler : 暂停目标topic:delay_topic_level_2
2025-10-15 11:35:43.644 INFO 25208 --- [ad | producer-1] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 发送至死信队列!原始事件: {"filterKey":"key","timeStamp":1760499343635,"message":"测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信","retryCount":3},时间戳:1760499343635,主题: hello-unicast-dlq-no-partition_dlq_topic, 分区: 0, 已重试次数: 3,最后一次异常信息:[消费失败] 模拟业务异常
[死信捕获] 死信Topic: hello-unicast-dlq-no-partition_dlq_topic, 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:45.079 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760499345079
2025-10-15 11:35:45.172 INFO 25208 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_2
2025-10-15 11:35:45.172 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 恢复成功,topic: delay_topic_level_2
2025-10-15 11:35:45.173 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 恢复目标topic消费:delay_topic_level_2
2025-10-15 11:35:45.174 INFO 25208 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: hello-unicast-dlq-no-partition
消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信
2025-10-15 11:35:45.192 WARN 25208 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 3/3): [消费失败] 模拟业务异常
2025-10-15 11:35:45.193 ERROR 25208 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event failed after 3 retries, sent to DLQ
2025-10-15 11:35:45.206 INFO 25208 --- [ad | producer-1] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 发送至死信队列!原始事件: {"filterKey":"key","timeStamp":1760499345192,"message":"测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信","retryCount":3},时间戳:1760499345193,主题: hello-unicast-dlq-no-partition_dlq_topic, 分区: 0, 已重试次数: 3,最后一次异常信息:[消费失败] 模拟业务异常
[死信捕获] 死信Topic: hello-unicast-dlq-no-partition_dlq_topic, 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信@Slf4j
@RequiredArgsConstructor
public class DLQEventHandlerWrapper implements EventHandler {
// 原事件处理器
private final EventHandler delegate;
// DLQ配置
private final DLQConfig dlqConfig;
// 事件中心(用于发送死信)
private final String mainTopic;
// 当前主Topic
private final KafkaEventCenter eventCenter;
private String lastException;
/**
* 处理事件,包含重试与死信发送逻辑。
* <p>核心逻辑:
* 1. 若DLQ未启用,直接调用原始处理器;
* 2. 若启用,按配置重试处理事件;
* 3. 重试耗尽后发送事件到DLQ。
*
* @param event 待处理的事件
*/
@Override
public void handleEvent(Event event) {
if (!dlqConfig.isEnabled()) {
delegate.handleEvent(event);
// 未启用DLQ,直接执行原逻辑
return;
}
int retryCount = event.getRetryCount();
boolean success = false;
while (retryCount < dlqConfig.getMaxRetries()) {
try {
delegate.handleEvent(event);
// 执行原事件处理
success = true;
break;
} catch (Exception e) {
retryCount++;
event.setRetryCount(retryCount);
log.warn("Event handle failed (attempt {}/{}): {}",
retryCount, dlqConfig.getMaxRetries(), e.getMessage());
lastException=e.getMessage();
if (retryCount >= dlqConfig.getMaxRetries()) {
// 达到最大重试次数,发送死信
break;
}
// 计算重试延时
long delay = dlqConfig.getRetryStrategy().getNextDelay(retryCount);
eventCenter.sendDelay(mainTopic,event,delay,new FlexibleMatchStrategy(
DelayQueueStrategy.ApproximationMode.UP));
sendDelay(event,delay);
}
}
if (!success) {
String dlqTopic = mainTopic + "_dlq_topic";
sendToDLQ(event, retryCount,lastException,dlqTopic);
log.error("Event failed after {} retries, sent to DLQ"
, dlqConfig.getMaxRetries());
}
}
/**
* 将事件发送至死信队列
*/
private void sendToDLQ(Event event,int retryCount,String lastException,String dlqTopic) {
// 构造自定义EventFuture回调,捕获发送结果
EventFuture dlqEventFuture = new EventFuture() {
@Override
public void onSuccess(EventCenterSendResult result) {
log.info("发送至死信队列!原始事件: {},时间戳:{},主题: {}, 分区: {}, 已重试次数: {},最后一次异常信息:{}",
event.toString(),result.getTimestamp(),result.getTopic(), result.getPartition(), retryCount, lastException);
}
@Override
public void onFailure(Throwable throwable) {
// 关键:记录重试次数和发送异常
log.error("死信消息发送失败!原始事件: {}, 已重试次数: {}, 异常信息: {}",
event.toString(), retryCount, throwable.getMessage(), throwable);
}
};
try {
// 调用带回调的send方法(匹配参数:topic, event, eventFuture)
eventCenter.send(dlqTopic, event, dlqEventFuture);
} catch (Exception e) {
// 处理send方法本身的异常(如参数错误)
log.error("调用死信发送方法时发生异常,已重试次数: {}", retryCount, e);
}
}
private void sendDelay(Event event, long delay) {
log.info("事件延时 {}ms 后重试", delay);
// TODO: 实际延时队列实现
}
}@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
@ComponentScan(basePackages = {
"com.tplink.nbu.demo.basicspringboot",
"com.tplink.smb.eventcenter.port.kafka.deadletter",
"com.tplink.smb.eventcenter.api.config"
})
public class DelayDeadTest implements CommandLineRunner {
@Autowired
private KafkaEventCenter eventCenter;
@Autowired
private DLQConfig deadLetterConfig;
// 所有测试主Topic(对应不同重载方法)
String mainTopic = "hello-unicast-dlq-no-partition"; // registerUnicast(DLQ, 无Partition)
public static void main(String[] args) {
SpringApplication.run(DelayDeadTest.class, args);
}
@Override
public void run(String... args) throws Exception {
eventCenter.registerDelayConsumer();
registerDLQConsumers(); // 注册所有带死信的消费者(模拟失败)
registerDLQListeners(); // 注册所有死信Topic的监听
sendTestMessagesToAllTopics();// 发送测试消息触发死信
}
/**
* 注册所有带死信的消费者
*/
private void registerDLQConsumers() {
// 模拟消费失败的Handler(所有测试共用)
EventHandler failingHandler = event -> {
System.out.println(" 消息: " + event.getMessage());
throw new RuntimeException("[消费失败] 模拟业务异常 ");
};
// -------------------------- 1. registerUnicast(DLQ, 无PartitionAssignorMode) --------------------------
eventCenter.registerUnicast(
mainTopic, // 主Topic
"group-unicast-dlq-no-part", // 消费者组(手动指定)
failingHandler,
ForkJoinPool.commonPool(), // 线程池
deadLetterConfig // 死信配置
);
}
/**
* 注册所有死信Topic的监听(死信Topic规则:主Topic + "_dlq_topic")
*/
private void registerDLQListeners() {
String dlqTopic = mainTopic + "_dlq_topic"; // 死信Topic(与业务代码规则一致)
eventCenter.registerUnicast(
dlqTopic,
"dlq-group-" + mainTopic, // 死信消费者组(唯一即可)
event -> System.out.println("[死信捕获] 死信Topic: " + dlqTopic + ", 消息: " + event.getMessage()),
ForkJoinPool.commonPool()
);
}
/**
* 给所有主Topic发送测试消息(触发消费失败)
*/
private void sendTestMessagesToAllTopics() {
Event event = new Event(
"key",
"测试消息(主Topic: " + mainTopic + ")- 会触发死信"
);
eventCenter.send(mainTopic, event);
System.out.println("[消息发送] 主Topic: " + mainTopic + ", 内容: " + event.getMessage());
}
}请分析并解决,为什么只发送了一条消息,但死信队列中出现不只一条消息