无法为发布商实施Spring Retry Logic

时间:2019-07-25 17:19:40

标签: java spring spring-jms jmstemplate

我有一个带有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方法。 对于如何实现此重试逻辑的任何指导将不胜感激。

3 个答案:

答案 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());