1

Мне не удалось найти шаблон интеграции предприятия или рецепт, который способствует решению этой проблемы:Как вызвать веб-сервис после повторных поставок, израсходованных в Apache Camel?

После того, как попытки повторной доставки были исчерпаны, мне нужно отправить запрос веб-службы обратно исходному источнику, чтобы уведомить отправителя о неудачной доставке.

При исчерпании всех попыток повторной доставки следует перенести сообщение в очередь с мертвой буквой? Затем создайте нового пользователя, слушающего эту очередь DL? Нужна ли мне уникальная очередь мертвой буквы для каждой из моих исходных очередей сообщений? Должен ли я добавлять заголовок сообщения, отмечая очередь источника, прежде чем переместить его в очередь с мертвой буквой? Если все сообщения отправляются в одну очередь с мертвой буквой, как мой потребитель должен знать, куда отправить запрос веб-службы?

Можете ли вы указать мне на книгу, сообщение в блоге или статью? Каков предписанный подход?

Я работаю в действительно старой версии Fuse ESB, но я ожидаю, что решения в ServiceMix будут одинаково применимы.

Или, может быть, то, о чем я прошу, - это анти-шаблон или запах кода. Пожалуйста, порекомендуйте.

ответ

1

Если вы новичок в Camel и действительно хотите получить глубокое знание об этом, я бы порекомендовал Camel in Action, a book by Claus Ibsen. Там a second edition in the works, с 14 из 19 глав уже сделано, чтобы вы могли также сделать это.

Если это слишком много, онлайн-документация в порядке, вы можете узнать об основных принципах. Для обработки ошибок я рекомендую начать с general error handling page, а затем перейти на error handler docs и exception policy documentation.

Как правило, dead letter channel - это путь - Camel автоматически отправит DLC после того, как повторные попытки были исчерпаны, вам просто нужно определить DLC самостоятельно. И его название подразумевает, что это канал и на самом деле не нужно быть очередью - вы можете писать в файл, вызывать веб-сервис, отправлять сообщение в очередь сообщений или просто записывать в журналы, это полностью зависит от вас.

// error-handler DLC, will send to HTTP endpoint when retries are exhausted 
errorHandler(deadLetterChannel("http4://my.webservice.hos/path") 
    .useOriginalMessage() 
    .maximumRedeliveries(3) 
    .redeliveryDelay(5000)) 

// exception-clause DLC, will send to HTTP endpoint when retries are exhausted 
onException(NetworkException.class) 
    .handled(true) 
    .maximumRedeliveries(5) 
    .backOffMultiplier(3) 
    .redeliveryDelay(15000) 
    .to("http4://my.webservice.hos/otherpath"); 

Я сам всегда предпочитал иметь очередь сообщений, а затем потреблял оттуда для любого другого восстановления или отчетности. Я обычно включаю данные об отказе, такие как идентификатор обмена и идентификатор маршрута, заголовки сообщений, сообщение об ошибке, а иногда даже stacktrace. Получающееся сообщение, как вы можете себе представить, растет совсем немного, но оно значительно упрощает поиск и устранение неполадок и отладки, особенно в средах, где у вас есть довольно много компонентов и сервисов. Вот пример сообщение DLC из одного моих проектов:

public class DeadLetterChannelMessage { 
    private String timestamp = Times.nowInUtc().toString(); 
    private String exchangeId; 
    private String originalMessageBody; 
    private Map<String, Object> headers; 
    private String fromRouteId; 
    private String errorMessage; 
    private String stackTrace; 

    @RequiredByThirdPartyFramework("jackson") 
    private DeadLetterChannelMessage() { 
    } 

    @SuppressWarnings("ThrowableResultOfMethodCallIgnored") 
    public DeadLetterChannelMessage(Exchange e) { 
    exchangeId = e.getExchangeId(); 
    originalMessageBody = e.getIn().getBody(String.class); 
    headers = Collections.unmodifiableMap(e.getIn().getHeaders()); 
    fromRouteId = e.getFromRouteId(); 

    Optional.ofNullable(e.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class)) 
     .ifPresent(throwable -> { 
     errorMessage = throwable.getMessage(); 
     stackTrace = ExceptionUtils.getStackTrace(throwable); 
     }); 
    } 

    // getters 
} 

При потреблении из очереди мертвой буквы, маршрут ID может сказать, где неудача возникла из так что вы можете реализовать маршруты, которые являются специфическими для ошибок вручая приходят оттуда :

// general DLC handling route 
from("{{your.dlc.uri}}") 
    .routeId(ID_REPROCESSABLE_DLC_ROUTE) 
    .removeHeaders(Headers.ALL) 
    .unmarshal().json(JsonLibrary.Jackson, DeadLetterChannelMessage.class) 
    .toD("direct:reprocess_${body.fromRouteId}"); // error handling route 

// handle errors from `myRouteId` 
from("direct:reprocess_myRouteId") 
    .log("Error: ${body.errorMessage} for ${body.originalMessageBody}"); 
    // you'll probably do something better here, e.g. 
    // .convertBodyTo(WebServiceErrorReport.class) // requires a converter 
    // .process(e -> { //do some pre-processing, like setting headers/properties }) 
    // .toD("http4://web-service-uri/path"); // send to web-service 


// for routes that have no DLC handling supplied 
onException(DirectConsumerNotAvailableException.class) 
    .handled(true) 
    .useOriginalMessage() 
    .removeHeaders(Headers.ALL) 
    .to({{my.unreprocessable.dlc}}); // errors that cannot be recovered from