我有一个带有JMS Publisher的Spring Boot应用程序。发布者使用了发布者类中运行良好的重试逻辑。但是我想将其更改为Spring Retry Template,并将其放入连接工厂配置中。
我在弄清楚如何调用将RetryTemplate添加到Publisher类时遇到麻烦。
这是Configuration类:
@Configuration
@EnableRetry
public class PurchasedTransServicePublisherConfig {
@Value("${java.naming.factory.initial.publisher}")
private String context;
@Value("${java.naming.provider.url.publisher}")
private String providerURL;
@Value("${fedex.jms.LDAP.entryName.publisher}")
private String ldapEntryName;
private @Value("${jms.username.publisher:#{null}}") String jmsUserName;
private @Value("${jms.password.publisher:#{null}}") String jmsPassword;
@Value("${jms.destinationname.publisher}")
private String destinationName;
private static final Logger LOGGER = LoggerFactory.getLogger(PurchasedTransServicePublisherConfig.class);
@Autowired(required = false)
FxgCipherInitializer jmsParams;
@Bean
public JmsTemplate publisherJmsTemplate(final ConnectionFactory publisherConnectionFactory) {
final JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(publisherConnectionFactory);
jmsTemplate.setPubSubDomain(true);
jmsTemplate.setDefaultDestinationName(destinationName);
jmsTemplate.setSessionTransacted(true);
return jmsTemplate;
}
@Bean
public ConnectionFactory publisherConnectionFactory(final JndiTemplate publisherJndiTemplate) throws NamingException {
final ConnectionFactory connectionFactory = (ConnectionFactory) publisherJndiTemplate.getContext().lookup(ldapEntryName);
final UserCredentialsConnectionFactoryAdapter ucf = new UserCredentialsConnectionFactoryAdapter();
ucf.setUsername(((null != jmsParams) ? jmsParams.getUsername() : jmsUserName));
ucf.setPassword((null != jmsParams) ? jmsParams.getPassword() : jmsPassword);
ucf.setTargetConnectionFactory(connectionFactory);
return ucf;
}
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(2000l);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(2);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
@Bean
public JndiTemplate publisherJndiTemplate() {
final JndiTemplate jndiTemplate = new JndiTemplate();
final Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, context);
jndiProps.setProperty(Context.PROVIDER_URL, providerURL);
jndiTemplate.setEnvironment(jndiProps);
return jndiTemplate;
}
}
工作配置和RetryTemplate配置之间的唯一变化是添加了注释@EnableRetry
和方法retryTemplate
发布者类最初使用以下逻辑成功重试了发送消息:
private void send(final MessageCreator messageCreator) throws JMSException {
int sendAttempts = 0;
while (true) {
try {
jmsTemplate.send(messageCreator);
LOGGER.info("Message Successfully Published");
break;
} catch (RuntimeException e) {
LOGGER.error("Caught Runtime Exception: {}", e.getMessage());
sendAttempts++;
handleJmsExceptionRetry(e, sendAttempts);
}
}
}
我试图像这样实现重试模板:
private void send(final MessageCreator messageCreator) throws JMSException {
while (true) {
try {
publisherJmsTemplate.send(messageCreator);
LOGGER.info("Message Successfully Published");
break;
} catch (RuntimeException e) {
LOGGER.error("Caught Runtime Exception: {}", e.getMessage());
publisherRetryTemplate.execute(arg0 -> {
publisherJmsTemplate.send(messageCreator);
return null;
});
}
}
}
我为进行单元测试而创建的测试方法如下:
@Test
public void testPublishTmsTrip_WhenPublishFailsMultipleTimes() {
Mockito.doThrow(RuntimeException.class).when(mockJmsTemplate).send(mockMessageCreator);
boolean testBoolean = tmsTripPublisher.publishTmsTripMessageEvent("TEST message");
assertFalse(testBoolean);
}
问题是当到达publisherRetryTemplate.execute...
时,它不执行RetryTemplate方法。
对于如何实现此重试逻辑的任何指导将不胜感激。
答案 0 :(得分:0)
除非在方法上使用@EnableRetry
,否则不需要@Retryable
。
不清楚为什么会有while (true)
,或者为什么最初在重试模板的execute方法之外调用。
为什么不只是
private void send(final MessageCreator messageCreator) throws JMSException {
publisherRetryTemplate.execute(arg0 -> {
publisherJmsTemplate.send(messageCreator);
return null;
});
}
?
您不清楚“它不执行RetryTemplate方法”是什么意思。
答案 1 :(得分:0)
您不需要try-catch块来重试该请求。重试模板本身按照配置的请求重试请求(2个重试,超时2000ms)。
只需使用
private boolean send(final MessageCreator messageCreator) {
try {
publisherRetryTemplate.execute(arg0 -> {
publisherJmsTemplate.send(messageCreator);
LOGGER.info("Message Successfully Published");
return null;
});
} catch (Exception e) {
LOGGER.error("Caught Exception: {}", e);
// Error handling logic here
return false;
}
}
答案 2 :(得分:0)
好像您将声明式方法与程序化方法相混合。
只要您不使用用@EnableRetry
和@Retryable
注释的方法,就不需要在配置类中使用@Recover
。
如果对RetryTemplate
bean使用编程重试,则无需在catch块中的异常上调用RetryTemplate.execute()
方法。
在定义RetryTemplate
bean时,您只需要指定异常,该异常将启动execute()
回调中指定的新操作。
例如,如果要在RuntimeException上重试该操作,但又不想在CustomException上重试该操作,则必须在策略映射中指定它:
new ExceptionClassifierRetryPolicy().
setPolicyMap(ImmutableMap.<Class<? extends Throwable>, RetryPolicy> builder()
.put(CustomException.class, new NeverRetryPolicy())
.put(RuntimeException.class, new SimpleRetryPolicy(MAX_RETRY_ATTEMPTS))
.build());