Статьи

Повторите операции веб-службы с RequestHandlerRetryAdvice

1. Введение

Иногда при вызове веб-службы мы можем быть заинтересованы в повторении операции в случае возникновения ошибки. Используя Spring Integration, мы можем достичь этой функциональности с помощью класса RequestHandlerRetryAdvice . Этот класс позволит нам повторить операцию определенное количество раз, прежде чем отказаться и вызвать исключение. Этот пост покажет вам, как этого добиться.

Тестовое приложение будет вызывать веб-сервис, и если оно не ответит, оно будет ждать в течение указанного времени и будет пытаться повторить его, пока не получит ответ или не достигнет предела повторных попыток. Если предел достигнут, неудавшийся запрос будет сохранен в базе данных. Главным образом, этот пост показывает пример следующего:

Исходный код приложения можно найти на github .

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

2. Веб-сервис вызова

Вариант использования : клиент вызывает веб-сервис и получает ответ.

grafic1

Запрос поступает в систему обмена сообщениями через шлюз «вход в систему». Затем он достигает исходящего шлюза, вызывает веб-сервис и ожидает ответа. После получения ответ отправляется в канал ответа.

Изображение выше является результатом этой конфигурации:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<context:component-scan base-package="xpadro.spring.integration" />
 
<!-- Initial service request -->
<int:gateway id="systemEntry" default-request-channel="requestChannel"
    service-interface="xpadro.spring.integration.gateway.ClientService" />
     
<int:channel id="requestChannel">
    <int:queue />
</int:channel>
 
<int-ws:outbound-gateway id="marshallingGateway"
    request-channel="requestChannel" reply-channel="responseChannel"
    uri="http://localhost:8080/spring-ws/orders" marshaller="marshaller"
    unmarshaller="marshaller">
     
    <int:poller fixed-rate="500" />
</int-ws:outbound-gateway>
 
<oxm:jaxb2-marshaller id="marshaller" contextPath="xpadro.spring.integration.types" />
 
 
<!-- Service is running - Response received -->
<int:channel id="responseChannel" />
 
<int:service-activator ref="clientServiceActivator" method="handleServiceResult" input-channel="responseChannel" />

К каналу ответа подключен активатор службы, который просто регистрирует результат.

TestInvocation.java: отправляет запрос на входной шлюз

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@ContextConfiguration({"classpath:xpadro/spring/integration/config/int-config.xml",
    "classpath:xpadro/spring/integration/config/mongodb-config.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestInvocation {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
     
    @Autowired
    private ClientService service;
     
    @Test
    public void testInvocation() throws InterruptedException, ExecutionException {
        logger.info("Initiating service request...");
         
        ClientDataRequest request = new ClientDataRequest();
        request.setClientId("123");
        request.setProductId("XA-55");
        request.setQuantity(new BigInteger("5"));
 
        service.invoke(request);
         
        logger.info("Doing other stuff...");
        Thread.sleep(60000);
    }
}

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

3. Добавление повторных рекомендаций

Вариант использования : первоначальный запрос не выполнен, поскольку служба не активна. Мы будем повторять операцию до тех пор, пока от службы не будет получен ответ.

В этом случае нам нужно добавить рекомендацию повтора в исходящий шлюз веб-службы:

01
02
03
04
05
06
07
08
09
10
11
<int-ws:outbound-gateway id="marshallingGateway" interceptor="clientInterceptor"
    request-channel="requestChannel" reply-channel="responseChannel"
    uri="http://localhost:8080/spring-ws/orders" marshaller="marshaller"
    unmarshaller="marshaller">
     
    <int:poller fixed-rate="500" />
     
    <int-ws:request-handler-advice-chain>
        <ref bean="retryAdvice" />
    </int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>

Теперь исходящий шлюз веб-службы будет делегировать вызов рекомендации повторения, которая будет пытаться выполнить операцию столько раз, сколько указано, пока не получит ответ от службы. Давайте определим совет повторной попытки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<bean id="retryAdvice" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" >
    <property name="retryTemplate">
        <bean class="org.springframework.retry.support.RetryTemplate">
            <property name="backOffPolicy">
                <bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
                    <property name="backOffPeriod" value="4000" />
                </bean>
            </property>
            <property name="retryPolicy">
                <bean class="org.springframework.retry.policy.SimpleRetryPolicy">
                    <property name="maxAttempts" value="4" />
                </bean>
            </property>
        </bean>
    </property>
</bean>

Для достижения этой цели в совете используется RetryTemplate, предоставляемый проектом Spring Retry . Мы можем настроить его поведение путем определения политик отката и повторных попыток .

Политика отката : устанавливает интервал времени между каждой повторной попыткой. Более интересные типы:

  • FixedBackOffPolicy : используется в нашем примере. Он будет ждать одинаковое количество времени между каждой повторной попыткой.
  • ExponentialBackOffPolicy : начиная с определенного промежутка времени, он удваивает время при каждой повторной попытке. Вы можете изменить поведение по умолчанию, установив множитель.

Политика повторных попыток: определяет, сколько раз будет повторяться неудачная операция. Некоторые из типов:

  • SimpleRetryPolicy : используется в нашем примере. Указывает предел попыток повторной попытки.
  • ExceptionClassifierRetryPolicy : позволяет нам устанавливать различные maxAttempts в зависимости от вызванного исключения.
  • TimeoutRetryPolicy : повторять попытки до истечения времени ожидания.

4. Не повезло, регистрация неудачного запроса

Вариант использования : служба не будет восстановлена, сохраняя запрос к базе данных.

adv1

Последняя часть конфигурации следующая:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<!-- Log failed invocation -->
<int:service-activator ref="clientServiceActivator" method="handleFailedInvocation" input-channel="errorChannel" output-channel="logChannel" />
 
<int:channel id="logChannel" />
 
<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
    <constructor-arg>
        <bean class="com.mongodb.Mongo"/>
    </constructor-arg>
    <constructor-arg value="test"/>
</bean>
 
<int-mongodb:outbound-channel-adapter id="mongodbAdapter" channel="logChannel"
    collection-name="failedRequests" mongodb-factory="mongoDbFactory" />

Активатор службы, подписанный на канал ошибок, извлечет сообщение об ошибке и отправит его исходящему адаптеру, который вставит его в базу данных mongoDB. Активатор службы:

1
2
3
4
public Message<?> handleFailedInvocation(MessagingException exception) {
    logger.info("Failed to succesfully invoke service. Logging to DB...");
    return exception.getFailedMessage();
}

Если нам не удастся получить ответ от сервиса, запрос будет сохранен в базе данных:

MongoDB

6. Заключение

Мы узнали, как Spring Integration получает поддержку от проекта Spring Retry, чтобы добиться повторных попыток выполнения операций. Мы использовали цепочку int-ws: request-handler-advice-chain , но пространство имен ‘int’ также поддерживает этот элемент, чтобы добавить эту функциональность в другие типы конечных точек.