Grails 中的 Camel 异常处理

Camel exception handling in Grails

我目前在抽象 class 中完成异常处理,我的所有路由都继承了它。像这样:

    onException(SocketException,HttpOperationFailedException)
        .handled(true)
        .maximumRedeliveries(settings.maximumRedeliveries)
        .redeliverDelay(settings.redeliverDelay)
        .useCollisionAvoidance()
        .collisionAvoidanceFactor(settings.collisionAvoidanceFactor)
        .onRedelivery(redeliveryProcessor)
        .log('retry failed, sending to the route failed coordinator')
        .to(routeFailedCoordinator)

现在,我想根据不同的响应代码做一些不同的事情。对于 200 以外的所有代码,将抛出 HttpOperationFailedException。对于 4XX 代码,如果为该特定路由启用,我想将消息发送到失败队列并发送电子邮件。对于所有其他错误,我想通过重试周期。以下是适用于 4XX 错误的方法:

    onException(HttpOperationFailedException)
        .handled(true)
        .process { Exchange x ->
            HttpOperationFailedException ex = x.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class)
            log.debug("Caught a HttpOperationFailedException: statusCode=${ex?.statusCode}")
            ProducerTemplate producer = x.getContext().createProducerTemplate()
            if (ex?.statusCode >= 400 && ex?.statusCode < 500) {
                log.debug("Skipping retries ...")

                producer.send(routeFailedEndpoint, x)

                x.in.body = "Request:\n${x.in.body}\n\nResponse: ${ex.statusCode}\n${ex.responseBody}".toString()
                if (sendFailedEmailEnabled)
                    producer.send('direct:routeFailedEmailHandler', x)
            } else {
                producer.send(routeFailedRetryEndpoint, x)
            }
        }.stop()

如何像第一个代码片段那样添加重试代码?我尝试使用嵌套的 choice()...when()...otherwise() 子句并不断出现编译错误。

有人做过类似的事情吗?

这是我的带有嵌套 choice()..when()..otherwise() 子句的代码:

    onException(HttpOperationFailedException)
        .handled(true)
        .choice()
            .when { Exchange x ->
                HttpOperationFailedException ex = x.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class)
                log.debug("Caught a HttpOperationFailedException: statusCode=${ex?.statusCode}")
                if (ex?.statusCode >= 400 && ex?.statusCode < 500) {
                    log.debug("Skipping retries ...")
                    x.in.body = "Request:\n${x.in.body}\n\nResponse: ${ex.statusCode}\n${ex.responseBody}".toString()
                    return true // don't retry
                }

                log.debug("Performing retries ...")
                return false // do attempt retries
            }.choice()
                .when { !sendFailedEmailEnabled }.to(routeFailedEndpoint)
                .otherwise()
                    .multicast().to(routeFailedEndpoint, 'direct:routeFailedEmailHandler').endChoice()
            .otherwise()
                .getParent().getParent().getParent()
                .maximumRedeliveries(settings.maximumRedeliveries)
                .redeliverDelay(settings.redeliverDelay)
                .useCollisionAvoidance()
                .collisionAvoidanceFactor(settings.collisionAvoidanceFactor)
                .onRedelivery(redeliveryProcessor)
                .to(routeFailedCoordinator)

你必须有 2 个 onException 块:

  • 一个 onException 具有重新发送尝试的重新发送设置
  • 另一个 onException 处理异常并发送该电子邮件和您想要执行的操作。
  • 在两个 onException 块上使用 onWhen,以 return truefalse 在任何一种情况下基于该 http 状态代码。 onWhen 由 Camel 执行以知道要使用哪个 onException 块(你可以有更多,但首先要使用 return true)。

您可以在 Camel 网站或 Camel in Action 一书中找到更多详细信息,该书有一整章专门介绍错误处理。

谢谢克劳斯,你给我指明了正确的方向。

基本上,正如 Claus 所说,使用多个 onException 块,每个块使用一个 onWhen 子句 ...

    onException(HttpOperationFailedException)
        .onWhen(new Predicate() {
            public boolean matches(Exchange exchange) {
                HttpOperationFailedException ex = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class)
                log.debug("Caught an HttpOperationFailedException: statusCode=${ex?.statusCode}, processing 4XX error")
                return (ex?.statusCode >= 400 && ex?.statusCode < 500)
            }
        }).handled(true)
            .to(routeFailedEndpoint)
            .choice()
                .when { sendFailedEmailEnabled }.process(prepareFailureEmail).to('direct:routeFailedEmailHandler')

    onException(HttpOperationFailedException)
        .onWhen(new Predicate() {
            public boolean matches(Exchange exchange) {
                HttpOperationFailedException ex = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class)
                log.debug("Caught an HttpOperationFailedException: statusCode=${ex?.statusCode}, processing >=500 error")
                return (ex?.statusCode >= 500)
            }
        }).handled(true)
            .maximumRedeliveries(settings.maximumRedeliveries)
            .redeliverDelay(settings.redeliverDelay)
            .useCollisionAvoidance()
            .collisionAvoidanceFactor(settings.collisionAvoidanceFactor)
            .onRedelivery(redeliveryProcessor)
            .to(routeFailedCoordinator)