如何处理/拦截Spring Cloud Streams中抛出的异常(Elmhurst / 2.0)

时间:2018-05-24 10:08:45

标签: spring-integration spring-cloud-stream spring-kafka

我目前在处理Spring Cloud Streams(Elmhurst.RELEASE)中的消息处理期间抛出的异常时遇到问题。当我的应用程序在主处理方法中抛出异常时:

@SpringBootApplication
@EnableBinding(Processor.class)
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @StreamListener(Processor.INPUT)
    @SendTo(Processor.OUTPUT)
    public String process(String message) {
        externalService();
        return message.toUpperCase();
    }

    private void externalService() {
        throw new RuntimeException("An external call failed");
    }
}

我似乎完全无法在任何错误通道上捕获此异常。通过阅读文档,我预计我将能够在全局" errorChannel"上接收ErrorMessage。或者在特定的" input.myGroup.errors"信道。

我试图使用Spring Integrations监听器:

@ServiceActivator(inputChannel="errorChannel")
public ErrorMessage onError(ErrorMessage message) {
    return message;
}

与Spring Cloud Streams听众一样:

@StreamListener("errorChannel")
public ErrorMessage onError(ErrorMessage message) {
    return message;
}

没有成功。我还使用了各种配置设置组合,例如" errorChannelEnabled:true"," max-retries:1"等等,对我能够捕捉到的错误没有任何影响我的处理器(使用调试点"返回消息"检查)。在配置" bindings.error.destination:myErrorTopic"时,我甚至不会在主题中收到任何错误消息。正如SCS文件中所建议的那样。

唯一似乎可行的是" enableDlq:true"在绑定配置级别。然而,这不能满足能够确定原始异常的重要需求,因为发布到DLQ的内容仅具有带有完整堆栈跟踪的标头。我不想解析堆栈跟踪来找出原始异常的类型或消息。

我应该采取更好的方法吗?还是我犯了一些愚蠢的错误?我的总体目标是将带有实际异常类型和异常消息的异常消息发送到DLQ。

当然,我可以在每个Processor / Source / Sink方法的周围放置try / catch语句,然后手动路由到不同的绑定通道,但这在很大程度上损害了SCS框架的价值主张。 / p>

我发现this example自定义DLQ处理是this早期StackOverflow问题的一部分,这似乎符合我的需求。然而,即使在将配置迁移到更新的SCS之后,这似乎与SCS Elmhurst / 2.0和Spring Kafka绑定器完全不兼容。

编辑我添加了一个Github repository来重现我的错误,因为复制相同的核心代码与Gary的回答似乎不起作用。我开始怀疑它是否是POM依赖,配置或Kafka活页夹问题。这个repo使用与Gary的答案相同的核心代码,因为我认为看到问题和调试有点简单。

GARY'答案之后的编辑我已经接受了Gary的回答,因为它解决了我原来的问题(在配置中有绑定环境时解决当前的框架问题) 。然而,DLQ消息最终对我的情况毫无帮助。我最终订阅了" errorChannel"一旦我根据Gary的回答得到了这个工作,并从那里创建了一个自定义错误消息,我发送到正常绑定的SCS频道。

1 个答案:

答案 0 :(得分:1)

为什么你有return message;?你期待它去哪儿?

这对我来说很好......

@SpringBootApplication
@EnableBinding(Sink.class)
public class So50506586Application {

    public static void main(String[] args) {
        SpringApplication.run(So50506586Application.class, args);
    }

    @StreamListener(Sink.INPUT)
    public void listen(byte[] in) {
        throw new RuntimeException("fail");
    }

    @ServiceActivator(inputChannel = "errorChannel")
    public void errors(ErrorMessage em) {
        System.out.println(em);
    }

}
  

ErrorMessage [payload = org.springframework.messaging.MessagingException:调用com.example.So50506586Application#listen [1 args]时抛出异常;嵌套异常是java.lang.RuntimeException:fail,failedMessage = GenericMessage [payload = byte [3],headers = {kafka_offset = 0,...

     

...

     

引起:java.lang.RuntimeException:失败

     

...

修改

使用多个活页夹支持时(通过environment内容)是一个错误。全局错误通道不可见。

这有效......

spring:
  cloud:
      stream:
        bindings:
          input:
            destination: input
            group: myGroup7
            binder: kafka
#          error:
#            destination: errors
        kafka.bindings.input.consumer.autoCommitOnError: true
        kafka:
          binder:
            brokers: localhost:9092

(您不再需要使用Elmhurst的zkNodes)。

此外,您不应该使用遗留error.destination内容,因为发送到错误频道的消息现在是一个包含大量信息的丰富ErrorMessage对象,请使用dlq内容自动发布一个主题。

我们需要删除这些遗留错误目标属性。

这不起作用......

@ServiceActivator(inputChannel = "input.myGroup.errors")
public void errors(ErrorMessage em) {
    System.out.println(em);
}

...因为服务激活器在绑定的不同应用程序上下文中。

目前,唯一的解决方法是不使用environment样式的配置。如果您需要多个活页夹支持,我将无法为您提供解决方案。

BTW,使用Elmhurst(和kafka 0.11或更高版本),堆栈跟踪不再嵌入发送到DLQ的消息的有效负载中,它将作为kafka标头发送。