exceptions - Catching Any Exception

本文深入探讨了Java中异常处理的基本概念,通过实例演示了如何创建一个能捕获所有类型异常的处理器,解释了Exception类的方法如getMessage(), getLocalizedMessage(), toString()和printStackTrace()的使用,并提供了源代码和参考资料。

We can create a handler that catches any type of exception by catching the base-class exception type Exception

// exceptions/ExceptionMethods.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Demonstrating the Exception Methods

public class ExceptionMethods {
  public static void main(String[] args) {
    try {
      throw new Exception("My Exception");
    } catch (Exception e) {
      System.out.println("Caught Exception");
      System.out.println("getMessage():" + e.getMessage());
      System.out.println("getLocalizedMessage():" + e.getLocalizedMessage()); // note this output
      System.out.println("toString():" + e); // Throwable Instance Method
      System.out.println("printStackTrace():");
      e.printStackTrace(System.out);
    }
  }
}
/* My Output:
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
        at ExceptionMethods.main(ExceptionMethods.java:10)
*/
Throwable class's two methods source code:
    /**
     * Creates a localized description of this throwable.
     * Subclasses may override this method in order to produce a
     * locale-specific message.  For subclasses that do not override this
     * method, the default implementation returns the same result as
     * {@code getMessage()}.
     *
     * @return  The localized description of this throwable.
     * @since   JDK1.1
     */
    public String getLocalizedMessage() {
        return getMessage();
    }
    /**
     * Returns a short description of this throwable.
     * The result is the concatenation of:
     * <ul>
     * <li> the {@linkplain Class#getName() name} of the class of this object
     * <li> ": " (a colon and a space)
     * <li> the result of invoking this object's {@link #getLocalizedMessage}
     *      method
     * </ul>
     * If {@code getLocalizedMessage} returns {@code null}, then just
     * the class name is returned.
     *
     * @return a string representation of this throwable.
     */
    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }

references:

1. On Java 8 - Bruce Eckel

2. https://github.com/wangbingfeng/OnJava8-Examples/blob/master/exceptions/ExceptionMethods.java

3. http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/Throwable.java

 

消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信 2025-10-17 10:02:20.359 INFO 14608 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=1, 到期时间expirationTime=1760666541359 2025-10-17 10:02:20.365 INFO 14608 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 1000ms 后重试 2025-10-17 10:02:20.376 INFO 14608 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_1 2025-10-17 10:02:21.370 INFO 14608 --- [pool-2-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_1, currentTime=1760666541370 2025-10-17 10:02:21.386 INFO 14608 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_1 2025-10-17 10:02:21.388 INFO 14608 --- [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-17 10:02:21.406 INFO 14608 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=2, 到期时间expirationTime=1760666546406 2025-10-17 10:02:21.409 INFO 14608 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 5000ms 后重试 2025-10-17 10:02:21.422 INFO 14608 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_2 2025-10-17 10:02:26.417 INFO 14608 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760666546417 2025-10-17 10:02:26.525 INFO 14608 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_2 2025-10-17 10:02:26.527 INFO 14608 --- [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-17 10:02:26.549 INFO 14608 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666556549 2025-10-17 10:02:26.552 INFO 14608 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 事件延时 10000ms 后重试 2025-10-17 10:02:26.560 INFO 14608 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_3 2025-10-17 10:02:36.563 INFO 14608 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666556563 2025-10-17 10:02:36.718 INFO 14608 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_3 2025-10-17 10:02:36.719 INFO 14608 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: hello-unicast-dlq-no-partition 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信 2025-10-17 10:02:36.728 INFO 14608 --- [ad | producer-1] c.t.s.e.p.k.d.DLQEventHandlerWrapper : 发送至死信队列!原始事件: {"retryCount":4,"filterKey":"key","timeStamp":1760666556725,"message":"测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信"},时间戳:1760666556726,主题: hello-unicast-dlq-no-partition_dlq_topic, 分区: 0, 已重试次数: 3,最后一次异常信息:[消费失败] 模拟业务异常 [死信捕获] 死信Topic: hello-unicast-dlq-no-partition_dlq_topic, 消息: 测试消息(主Topic: hello-unicast-dlq-no-partition)- 会触发死信@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()); } }@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); return; } int retryCount = event.getRetryCount(); // 达到阈值直接进入死信 if (retryCount > dlqConfig.getMaxRetries()) { sendToDLQ(event, retryCount, lastException, mainTopic + "_dlq_topic"); return; } try { delegate.handleEvent(event); } catch (Exception e) { int newRetryCount = retryCount + 1; event.setRetryCount(newRetryCount); if (newRetryCount > dlqConfig.getMaxRetries()) { sendToDLQ(event, newRetryCount, e.getMessage(), mainTopic + "_dlq_topic"); } else { long delay = dlqConfig.getRetryStrategy().getNextDelay(newRetryCount); eventCenter.sendDelay(mainTopic, event, delay, new FlexibleMatchStrategy(DelayQueueStrategy.ApproximationMode.UP)); log.info("事件延时 {}ms 后重试", delay); } } } /** * 将事件发送至死信队列 */ 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 - 1 , lastException); } @Override public void onFailure(Throwable throwable) { // 关键:记录重试次数和发送异常 log.error("死信消息发送失败!原始事件: {}, 已重试次数: {}, 异常信息: {}", event.toString(), retryCount - 1 , throwable.getMessage(), throwable); } }; try { // 调用带回调的send方法(匹配参数:topic, event, eventFuture) eventCenter.send(dlqTopic, event, dlqEventFuture); } catch (Exception e) { // 处理send方法本身的异常(如参数错误) log.error("调用死信发送方法时发生异常", e); } } } 2025-10-17 10:04:47.425 INFO 19516 --- [nPool-worker-11] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:47.426 INFO 19516 --- [nPool-worker-11] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=1, 到期时间expirationTime=1760666688426 2025-10-17 10:04:47.426 INFO 19516 --- [onPool-worker-4] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:47.426 INFO 19516 --- [onPool-worker-4] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666697426 2025-10-17 10:04:47.446 INFO 19516 --- [nPool-worker-11] c.t.n.d.b.DLQGenericValidationApp : 处理CustomEvent事件 2025-10-17 10:04:47.447 ERROR 19516 --- [nPool-worker-11] c.t.s.e.core.GenericDataProcessor : Fail to process event, handler:DLQGenericEventHandlerWrapper java.lang.RuntimeException: retryCount值未更新! at com.tplink.smb.eventcenter.port.kafka.deadletter.DLQGenericEventHandlerWrapper.handleEvent(DLQGenericEventHandlerWrapper.java:56) ~[eventcenter.kafka-1.4.5002-test-SNAPSHOT.jar:1.4.5002-test-SNAPSHOT] at com.tplink.smb.eventcenter.port.kafka.deadletter.DLQGenericEventHandlerWrapper.handleEvent(DLQGenericEventHandlerWrapper.java:24) ~[eventcenter.kafka-1.4.5002-test-SNAPSHOT.jar:1.4.5002-test-SNAPSHOT] at com.tplink.smb.eventcenter.core.GenericDataProcessor.run(GenericDataProcessor.java:32) ~[eventcenter.core-1.4.5002-test-SNAPSHOT.jar:1.4.5002-test-SNAPSHOT] at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1386) [na:1.8.0_462-462] at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) [na:1.8.0_462-462] at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) [na:1.8.0_462-462] at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) [na:1.8.0_462-462] at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175) [na:1.8.0_462-462] 2025-10-17 10:04:47.450 INFO 19516 --- [nPool-worker-11] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":1,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:47.450 INFO 19516 --- [nPool-worker-11] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=1, 到期时间expirationTime=1760666688450 2025-10-17 10:04:47.451 INFO 19516 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_1 2025-10-17 10:04:47.452 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":0,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:47.452 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666697452 2025-10-17 10:04:47.454 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_3 2025-10-17 10:04:48.443 INFO 19516 --- [pool-2-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_1, currentTime=1760666688443 2025-10-17 10:04:48.474 INFO 19516 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_1 2025-10-17 10:04:48.475 INFO 19516 --- [pool-2-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:04:48.478 INFO 19516 --- [nPool-worker-25] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:04:48.479 INFO 19516 --- [nPool-worker-25] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:48.479 INFO 19516 --- [nPool-worker-25] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=2, 到期时间expirationTime=1760666693479 2025-10-17 10:04:48.479 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":2,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:48.479 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=2, 到期时间expirationTime=1760666693479 2025-10-17 10:04:48.493 INFO 19516 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_2 2025-10-17 10:04:53.482 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760666693482 2025-10-17 10:04:53.482 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:04:53.482 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760666693482 2025-10-17 10:04:53.500 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:53.500 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666703500 2025-10-17 10:04:53.608 INFO 19516 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_2 2025-10-17 10:04:53.610 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:04:53.615 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":3,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:53.616 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666703616 2025-10-17 10:04:57.431 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666697431 2025-10-17 10:04:57.679 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_3 2025-10-17 10:04:57.680 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:04:57.681 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:04:57.684 INFO 19516 --- [nPool-worker-25] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:57.684 INFO 19516 --- [nPool-worker-25] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=1, 到期时间expirationTime=1760666698684 2025-10-17 10:04:57.684 INFO 19516 --- [onPool-worker-4] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":1,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:57.684 INFO 19516 --- [onPool-worker-4] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=1, 到期时间expirationTime=1760666698684 2025-10-17 10:04:57.685 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_3 2025-10-17 10:04:57.686 INFO 19516 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_1 2025-10-17 10:04:58.694 INFO 19516 --- [pool-2-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_1, currentTime=1760666698694 2025-10-17 10:04:58.709 INFO 19516 --- [r_group_level_1] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_1 2025-10-17 10:04:58.710 INFO 19516 --- [pool-2-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:04:58.712 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:04:58.712 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=2, 到期时间expirationTime=1760666703712 2025-10-17 10:04:58.713 INFO 19516 --- [onPool-worker-4] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:04:58.728 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":2,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:04:58.728 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=2, 到期时间expirationTime=1760666703728 2025-10-17 10:04:58.729 INFO 19516 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_2 2025-10-17 10:05:03.508 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666703508 2025-10-17 10:05:03.508 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:05:03.529 INFO 19516 --- [ad | producer-1] .s.e.p.k.d.DLQGenericEventHandlerWrapper : 死信发送成功 | 主题: test-eventv2-topic_dlq_topic, 分区: 0, 重试次数: 4, 异常信息: null 2025-10-17 10:05:03.530 INFO 19516 --- [onPool-worker-4] c.t.n.d.b.DLQGenericValidationApp : 收到EventV2死信消息class com.tplink.smb.eventcenter.api.EventV2 2025-10-17 10:05:03.631 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666703631 2025-10-17 10:05:03.723 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_2, currentTime=1760666703723 2025-10-17 10:05:03.817 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_3 2025-10-17 10:05:03.817 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:05:03.836 INFO 19516 --- [ad | producer-1] .s.e.p.k.d.DLQGenericEventHandlerWrapper : 死信发送成功 | 主题: test-event-topic_dlq_topic, 分区: 0, 重试次数: 4, 异常信息: null 2025-10-17 10:05:03.836 INFO 19516 --- [onPool-worker-4] c.t.n.d.b.DLQGenericValidationApp : 收到Event死信消息Hello Event! 2025-10-17 10:05:03.864 INFO 19516 --- [r_group_level_2] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_2 2025-10-17 10:05:03.864 INFO 19516 --- [pool-5-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:05:03.865 INFO 19516 --- [onPool-worker-4] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:05:03.866 INFO 19516 --- [onPool-worker-4] c.t.n.d.b.DLQGenericValidationApp : 处理EventV2事件: Hello EventV2! 2025-10-17 10:05:03.868 INFO 19516 --- [onPool-worker-4] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666713868 2025-10-17 10:05:03.868 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 处理Event事件: {"retryCount":3,"filterKey":"key1","timeStamp":0,"message":"Hello Event!"} 2025-10-17 10:05:03.868 INFO 19516 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : 延迟等级level=3, 到期时间expirationTime=1760666713868 2025-10-17 10:05:03.882 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际暂停成功,topic: delay_topic_level_3 2025-10-17 10:05:13.877 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666713877 2025-10-17 10:05:13.878 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-event-topic 2025-10-17 10:05:13.878 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.delay.DelayHandler : 延迟任务执行:targetTopic=delay_topic_level_3, currentTime=1760666713878 2025-10-17 10:05:13.911 INFO 19516 --- [ad | producer-1] .s.e.p.k.d.DLQGenericEventHandlerWrapper : 死信发送成功 | 主题: test-event-topic_dlq_topic, 分区: 0, 重试次数: 4, 异常信息: null 2025-10-17 10:05:13.911 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 收到Event死信消息Hello Event! 2025-10-17 10:05:14.126 INFO 19516 --- [r_group_level_3] c.t.s.e.p.k.c.GenericKafkaConsumerTask : 实际恢复成功,topic: delay_topic_level_3 2025-10-17 10:05:14.126 INFO 19516 --- [pool-8-thread-1] c.t.s.e.port.kafka.KafkaEventCenter : 延迟事件已转发,targetTopic: test-eventv2-topic 2025-10-17 10:05:14.158 INFO 19516 --- [ad | producer-1] .s.e.p.k.d.DLQGenericEventHandlerWrapper : 死信发送成功 | 主题: test-eventv2-topic_dlq_topic, 分区: 0, 重试次数: 4, 异常信息: null 2025-10-17 10:05:14.158 INFO 19516 --- [nPool-worker-22] c.t.n.d.b.DLQGenericValidationApp : 收到EventV2死信消息class com.tplink.smb.eventcenter.api.EventV2@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); return; } int retryCount = event.getRetryCount(); // 达到阈值直接进入死信 if (retryCount > dlqConfig.getMaxRetries()) { sendToDLQ(event, retryCount, lastException, mainTopic + "_dlq_topic"); return; } try { delegate.handleEvent(event); } catch (Exception e) { int newRetryCount = retryCount + 1; event.setRetryCount(newRetryCount); if (newRetryCount > dlqConfig.getMaxRetries()) { sendToDLQ(event, newRetryCount, e.getMessage(), mainTopic + "_dlq_topic"); } else { long delay = dlqConfig.getRetryStrategy().getNextDelay(newRetryCount); eventCenter.sendDelay(mainTopic, event, delay, new FlexibleMatchStrategy(DelayQueueStrategy.ApproximationMode.UP)); log.info("事件延时 {}ms 后重试", delay); } } } /** * 将事件发送至死信队列 */ 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 - 1 , lastException); } @Override public void onFailure(Throwable throwable) { // 关键:记录重试次数和发送异常 log.error("死信消息发送失败!原始事件: {}, 已重试次数: {}, 异常信息: {}", event.toString(), retryCount - 1 , throwable.getMessage(), throwable); } }; try { // 调用带回调的send方法(匹配参数:topic, event, eventFuture) eventCenter.send(dlqTopic, event, dlqEventFuture); } catch (Exception e) { // 处理send方法本身的异常(如参数错误) log.error("调用死信发送方法时发生异常", e); } } }@Slf4j @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 DLQGenericValidationApp implements CommandLineRunner { @Autowired private KafkaEventCenter eventCenter; @Autowired private DLQConfig deadLetterConfig; // 定义三个主Topic及对应的死信Topic private static final String EVENT_TOPIC = "test-event-topic"; private static final String EVENTV2_TOPIC = "test-eventv2-topic"; private static final String CUSTOM_TOPIC = "test-custom-topic"; private static final String EVENT_DLQ_TOPIC = EVENT_TOPIC + "_dlq_topic"; private static final String EVENTV2_DLQ_TOPIC = EVENTV2_TOPIC + "_dlq_topic"; private static final String CUSTOM_DLQ_TOPIC = CUSTOM_TOPIC + "_dlq_topic"; public static void main(String[] args) { SpringApplication.run(DLQGenericValidationApp.class, args); } @Override public void run(String... args) throws Exception { eventCenter.registerDelayConsumer(); // 步骤1:为三类事件分别注册带DLQ的泛型消费者 registerEventConsumer(); registerEventV2Consumer(); registerCustomEventConsumer(); // 步骤2:为三个死信Topic分别注册监听 registerEventDLQListener(); registerEventV2DLQListener(); registerCustomDLQListener(); // 步骤3:发送三类测试事件到各自主Topic sendTestEvents(); } /** * 注册Event类型的消费者(主Topic: EVENT_TOPIC) */ private void registerEventConsumer() { GenericEventHandler<Event> eventHandler = event -> { log.info("处理Event事件: {}", event); throw new RuntimeException("模拟Event处理失败"); // 主动抛异常触发重试 }; eventCenter.registerUnicastGenerically( EVENT_TOPIC, // Event主Topic "event-consumer-group", eventHandler, ForkJoinPool.commonPool(), PartitionAssignorMode.COOPERATIVE_STICKY, Event.class, deadLetterConfig ); } /** * 注册EventV2类型的消费者(主Topic: EVENTV2_TOPIC) */ private void registerEventV2Consumer() { GenericEventHandler<EventV2> eventV2Handler = event -> { log.info("处理EventV2事件: {}", event.getMessage()); throw new RuntimeException("模拟EventV2处理失败"); // 主动抛异常触发重试 }; eventCenter.registerUnicast( EVENTV2_TOPIC, // EventV2主Topic "eventv2-consumer-group", // EventV2消费者组 eventV2Handler, ForkJoinPool.commonPool(), PartitionAssignorMode.COOPERATIVE_STICKY, SerializeEnum.KRYO, deadLetterConfig ); } /** * 注册CustomEvent类型的消费者(主Topic: CUSTOM_TOPIC) */ private void registerCustomEventConsumer() { GenericEventHandler<CustomEvent> customHandler = event -> { log.info("处理CustomEvent事件"); throw new RuntimeException("模拟CustomEvent处理失败"); // 主动抛异常触发重试 }; eventCenter.registerUnicastGenerically( CUSTOM_TOPIC, // Custom主Topic "custom-consumer-group", // Custom消费者组 customHandler, ForkJoinPool.commonPool(), PartitionAssignorMode.COOPERATIVE_STICKY, CustomEvent.class, deadLetterConfig ); } /** * 注册Event死信队列监听(验证Event死信) */ private void registerEventDLQListener() { eventCenter.registerUnicast( EVENT_DLQ_TOPIC, // Event死信Topic "event-dlq-group", // Event死信消费者组 event-> log.info("收到Event死信消息{}",event.getMessage()), // 明确Event类型 ForkJoinPool.commonPool()// 与Event发送时的序列化方式一致(假设Event用JSON) ); } /** * 注册EventV2死信队列监听(验证EventV2死信) */ private void registerEventV2DLQListener() { eventCenter.registerUnicast( EVENTV2_DLQ_TOPIC, // EventV2死信Topic "eventv2-dlq-group", // EventV2死信消费者组 event -> log.info("收到EventV2死信消息{}",event.getClass()), ForkJoinPool.commonPool(), SerializeEnum.KRYO ); } /** * 注册CustomEvent死信队列监听(验证CustomEvent死信) */ private void registerCustomDLQListener() { eventCenter.registerUnicastGenerically( CUSTOM_DLQ_TOPIC, // Custom死信Topic "custom-dlq-group", // Custom死信消费者组 event -> log.info("收到Custom死信消息{}",event.content), ForkJoinPool.commonPool(), CustomEvent.class ); } /** * 发送三类测试事件到各自主Topic */ private void sendTestEvents() { // 1. 发送Event到EVENT_TOPIC(JSON序列化) Event event = new Event("key1", "Hello Event!"); eventCenter.sendGenerically(EVENT_TOPIC, event, buildTestFuture("Event")); // 2. 发送EventV2到EVENTV2_TOPIC(Kryo序列化) EventV2<String> eventV2 = new EventV2<>("key2", "Hello EventV2!"); eventCenter.send(EVENTV2_TOPIC, eventV2, buildTestFuture("EventV2"),SerializeEnum.KRYO); // 3. 发送CustomEvent到CUSTOM_TOPIC(JSON序列化) CustomEvent customEvent = new CustomEvent("key3", "Hello CustomEvent!"); eventCenter.sendGenerically(CUSTOM_TOPIC, customEvent, buildTestFuture("CustomEvent")); } /** * 构建测试用的GenericEventFuture */ private <U> GenericEventFuture<U> buildTestFuture(String eventType) { return new GenericEventFuture<U>() { @Override public void onSuccess(GenericEventCenterSendResult<U> result) { log.info("[{}] 发送成功 | 主题: {}, 分区: {}", eventType, result.getTopic(), result.getPartition()); } @Override public void onFailure(Throwable throwable) { log.error("[{}] 发送失败 | 原因: {}", eventType, throwable.getMessage()); } }; }请分析,为什么泛型发送测试时,异常显示为null
最新发布
10-18
import asyncio from functools import wraps import aiohttp from flask import Flask, jsonify from coding_practice.aw.exception import ConnectionException #url="https://api.gametools.network/bf1/all/?format_values=false&lang=zh-tw&platform=pc&name=2017c&'" url="https://api.gametools" app=Flask(__name__) #装饰器 def decorator_fuction(func): @wraps(func) async def wrapper(*args, **kwargs): result=await func(*args, **kwargs) result_dict=result.get_json() return result_dict['data'] return wrapper def response(status,message="",data=None): response_dict={ 'status':status, 'message':message, 'data':data } return jsonify(response_dict) # if not isinstance(data,dict): # response_dict['s # # if data: # # return jsonify({'status':'success','data':data,'code':200}) # # # # else: # # return jsonify({'status':'fail','data':data,'code':404}) # # else: # return jsonify({'status':'data not dict','data':data,'code':502}) async def getData(Sem,Url,Result_Dict): async with Sem: try: async with aiohttp.ClientSession() as session: async with session.get(Url) as resp: resp_dict = await resp.json(content_type=None) if len(resp_dict) > 0: Result_Dict["获取到的数据如下:"]=resp_dict except aiohttp.ClientResponseError as response_err: #print(f"出现响应错误:{response_err}") return 502,f"出现超时错误:{response_err}","" except aiohttp.ClientTimeout as timeout_err: #print(f"出现超时错误:{timeout_err}") return 504,f"出现超时错误:{timeout_err}","" except aiohttp.ClientConnectionError as conn_err: #print(f"出现连接错误:{conn_err}") return 500,f"出现连接错误:{conn_err}","" except Exception as e: #print(f"异常为:{e}") return -1,f"其他异常为:{e}","" return resp.status,"success",Result_Dict @app.route('/getDataFromRemote') @decorator_fuction async def getDataFromRemote(): result_dict={} sem=asyncio.Semaphore(3) tasks=[asyncio.create_task(getData(sem,url,result_dict))] result=await asyncio.gather(*tasks) response_status,message,result_dict=result[0] return response(response_status,message,result_dict) if __name__ == '__main__': app.run(host='0.0.0.0',port=3040,debug=False)传入测试用的错误url报错C:\Users\30342\AppData\Local\Programs\Python\Python39\python.exe C:\Users\30342\Desktop\trial\coding_practice\coding_practice\scripts\flask+aiohttp.py * Serving Flask app 'flask+aiohttp' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:3040 * Running on http://192.168.0.109:3040 Press CTRL+C to quit [2025-08-21 15:51:57,884] ERROR in app: Exception on /getDataFromRemote [GET] Traceback (most recent call last): File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\connector.py", line 1169, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\connector.py", line 880, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 856, in getaddrinfo return await self.run_in_executor( File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\thread.py", line 52, in run result = self.fn(*self.args, **self.kwargs) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\socket.py", line 953, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno 11001] getaddrinfo failed The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\30342\Desktop\trial\coding_practice\coding_practice\scripts\flask+aiohttp.py", line 53, in getData async with session.get(Url) as resp: File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\client.py", line 1187, in __aenter__ self._resp = await self._coro File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\client.py", line 574, in _request conn = await self._connector.connect( File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\connector.py", line 907, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\aiohttp\connector.py", line 1183, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.gametools:443 ssl:default [getaddrinfo failed] During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\flask\app.py", line 1511, in wsgi_app response = self.full_dispatch_request() File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\flask\app.py", line 919, in full_dispatch_request rv = self.handle_user_exception(e) File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\flask\app.py", line 917, in full_dispatch_request rv = self.dispatch_request() File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\flask\app.py", line 902, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\asgiref\sync.py", line 262, in __call__ return call_result.result() File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\_base.py", line 433, in result return self.__get_result() File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\_base.py", line 389, in __get_result raise self._exception File "C:\Users\30342\AppData\Local\Programs\Python\Python39\lib\site-packages\asgiref\sync.py", line 302, in main_wrap result = await awaitable File "C:\Users\30342\Desktop\trial\coding_practice\coding_practice\scripts\flask+aiohttp.py", line 17, in wrapper result=await func(*args, **kwargs) File "C:\Users\30342\Desktop\trial\coding_practice\coding_practice\scripts\flask+aiohttp.py", line 82, in getDataFromRemote result=await asyncio.gather(*tasks) File "C:\Users\30342\Desktop\trial\coding_practice\coding_practice\scripts\flask+aiohttp.py", line 61, in getData except aiohttp.ClientTimeout as timeout_err: TypeError: catching classes that do not inherit from BaseException is not allowed 192.168.0.109 - - [21/Aug/2025 15:51:57] "GET /getDataFromRemote HTTP/1.1" 500 -
08-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值