1. Введение
Иногда при вызове веб-службы мы можем быть заинтересованы в повторении операции в случае возникновения ошибки. Используя Spring Integration, мы можем достичь этой функциональности с помощью класса RequestHandlerRetryAdvice . Этот класс позволит нам повторить операцию определенное количество раз, прежде чем отказаться и вызвать исключение. Этот пост покажет вам, как этого добиться.
Тестовое приложение будет вызывать веб-сервис, и если оно не ответит, оно будет ждать в течение указанного времени и будет пытаться повторить его, пока не получит ответ или не достигнет предела повторных попыток. Если предел достигнут, неудавшийся запрос будет сохранен в базе данных. Главным образом, этот пост показывает пример следующего:
- Использование исходящего шлюза для вызова веб-службы
- Конфигурирование рекомендаций по повторным попыткам при необходимости повторить операцию
- MongoDB интеграция.
Исходный код приложения можно найти на github .
Вы также можете получить исходный код проекта веб-службы, который вызывается приложением на github .
2. Веб-сервис вызова
Вариант использования : клиент вызывает веб-сервис и получает ответ.
Запрос поступает в систему обмена сообщениями через шлюз «вход в систему». Затем он достигает исходящего шлюза, вызывает веб-сервис и ожидает ответа. После получения ответ отправляется в канал ответа.
Изображение выше является результатом этой конфигурации:
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" 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" 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. Не повезло, регистрация неудачного запроса
Вариант использования : служба не будет восстановлена, сохраняя запрос к базе данных.
Последняя часть конфигурации следующая:
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(); } |
Если нам не удастся получить ответ от сервиса, запрос будет сохранен в базе данных:
6. Заключение
Мы узнали, как Spring Integration получает поддержку от проекта Spring Retry, чтобы добиться повторных попыток выполнения операций. Мы использовали цепочку int-ws: request-handler-advice-chain , но пространство имен ‘int’ также поддерживает этот элемент, чтобы добавить эту функциональность в другие типы конечных точек.