春季重试与for循环

huangapple go评论73阅读模式
英文:

Spring Retry with for loop

问题

以下是您提供的代码部分的中文翻译:

@Configuration
@EnableRetry
public class RetryTemplateConfig {

    @Value("${spring.retry.attempts}")
    private int maxAttempts;

    @Value("${spring.retry.period}")
    private long backOffPeriod;

    @Bean
    public RetryTemplate retryTemplate() {
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(maxAttempts);
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(backOffPeriod);
        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }
}

@Scheduled(cron = "${schedule.cron.update.users}")
public void sendToUsers() throws Exception {
    log.info("Scheduled sending for users started");
    try {
        mailSender.sendToUsers();
    } catch (MessagingException | IOException | TemplateException e) {
        log.error("Error occurred while sending email message to users: {}", e.toString());
    }
    log.info("Scheduled sending for users finished");
}

public void sendToUsers() throws Exception {
    String subject = reportGenerationService.getEmailSubjectForUser();
    Map<String, List<BadUtmMark>> utmMarksGroupedByEmail = userService.getUtmMarksGroupedByEmail(LocalDate.now());
    if (utmMarksGroupedByEmail.isEmpty()) {
        log.info("All's fine - no broken utm marks in database. Emails to users will not be sent.");
    }
    for (Map.Entry<String, List<BadUtmMark>> pair : utmMarksGroupedByEmail.entrySet()) {
        retryTemplate.execute(retryContext -> {
            String emailTo = pair.getKey();
            List<BadUtmMark> badUtmMarks = pair.getValue();
            String report = reportGenerationService.getReportForUser(emailTo, badUtmMarks, template);
            MimeMessage mimeMessage = getMimeMessage(subject, report, Collections.singletonList(emailTo));
            log.info("Message will be sent to: {}; from: {}; with subject: {}", pair.getKey(), from, subject);
            mailSender.send(mimeMessage);
            return true;
        });
    }
}

@Scheduled(cron = "${schedule.cron.update.users}")
public void sendToUsers() throws Exception {
    log.info("Scheduled sending for users started");
    try {
        retryTemplate.execute(retryContext -> {
            log.warn("Sending email to users. Attempt: {}", retryContext.getRetryCount());
            mailSender.sendToUsers();
            return true;
        });
    } catch (MessagingException | IOException | TemplateException e) {
        log.error("Error occurred while sending email message to users: {}", e.toString());
    }
    log.info("Scheduled sending for users finished");
}

预期行为:我希望为5位用户发送电子邮件。如果发生错误,则尝试将电子邮件重试发送5次,然后如果重试次数用尽,则继续循环并为下一个用户发送电子邮件。

实际行为:如果发生错误,服务将捕获异常并停止循环。

如果将重试逻辑移至此方法中:

@Scheduled(cron = "${schedule.cron.update.users}")
public void sendToUsers() throws Exception {
    log.info("Scheduled sending for users started");
    try {
        retryTemplate.execute(retryContext -> {
            log.warn("Sending email to users. Attempt: {}", retryContext.getRetryCount());
            mailSender.sendToUsers();
            return true;
        });
    } catch (MessagingException | IOException | TemplateException e) {
        log.error("Error occurred while sending email message to users: {}", e.toString());
    }
    log.info("Scheduled sending for users finished");
}

这样做效果更好,但仍不符合我的预期。通过此方式,如果发生错误,服务将尝试将电子邮件重试发送5次,但如果重试次数用尽,则服务将停止循环。因此,如果其中一个用户发生错误,服务将尝试为该用户再次发送5次电子邮件,然后停止,忽略地图中的其他用户。

但我希望为映射中的每个电子邮件执行5次重试。 我该如何做到这一点?

英文:

My RetryTemplate config:

@Configuration
@EnableRetry
public class RetryTemplateConfig {
@Value(&quot;${spring.retry.attempts}&quot;)
private int maxAttempts;
@Value(&quot;${spring.retry.period}&quot;)
private long backOffPeriod;
@Bean
public RetryTemplate retryTemplate() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(maxAttempts);
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(backOffPeriod);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
return retryTemplate;
}
}

Scheduled method which invokes method that uses Retry:

@Scheduled(cron = &quot;${schedule.cron.update.users}&quot;)
public void sendToUsers() throws Exception {
log.info(&quot;Scheduled sending for users started&quot;);
try {
mailSender.sendToUsers();
} catch (MessagingException | IOException | TemplateException e) {
log.error(&quot;Error occurred while sending email message to users: {}&quot;, e.toString());
}
log.info(&quot;Scheduled sending for users finished&quot;);
}

Method in which i want to use RetryTemplate:

public void sendToUsers() throws Exception {
String subject = reportGenerationService.getEmailSubjectForUser();
Map&lt;String, List&lt;BadUtmMark&gt;&gt; utmMarksGroupedByEmail = userService.getUtmMarksGroupedByEmail(LocalDate.now());
if (utmMarksGroupedByEmail.isEmpty()) {
log.info(&quot;All&#39;s fine - no broken utm marks in database. Emails to users will not be send.&quot;);
}
for (Map.Entry&lt;String, List&lt;BadUtmMark&gt;&gt; pair : utmMarksGroupedByEmail.entrySet()) {
retryTemplate.execute(retryContext -&gt; {
String emailTo = pair.getKey();
List&lt;BadUtmMark&gt; badUtmMarks = pair.getValue();
String report = reportGenerationService.getReportForUser(emailTo, badUtmMarks, template);
MimeMessage mimeMessage = getMimeMessage(subject, report, Collections.singletonList(emailTo));
log.info(&quot;Message will be sent to: {}; from: {}; with subject: {}&quot;, pair.getKey(), from, subject);
mailSender.send(mimeMessage);
return true;
});
}
}

Expected behaviour: I want to send emails for 5 people. If error occurs, then try to send email to this user 5 more times, and then if retries exhausted, keep going and send email for next user in for loop.

Really behaviour: If error occurs, service will catch exception and stop looping.

If i'll move retry logic to this method:

@Scheduled(cron = &quot;${schedule.cron.update.users}&quot;)
public void sendToUsers() throws Exception {
log.info(&quot;Scheduled sending for users started&quot;);
try {
retryTemplate.execute(retryContext - &gt; {
log.warn(&quot;Sending email to users. Attempt: {}&quot;, retryContext.getRetryCount());
mailSender.sendToUsers();
return true;
});
} catch (MessagingException | IOException | TemplateException e) {
log.error(&quot;Error occurred while sending email message to users: {}&quot;, e.toString());
}
log.info(&quot;Scheduled sending for users finished&quot;);
}

It works better, but still not what I expect. With this case if error occurs, service will try to send email 5 more times, but if retries exhausted, service will stop looping. So, if error occurs with one of the user, the service will try to send 5 more times for this user, and then will stop, ignoring other users in map.

But I want to do 5 retries for each email in my map. How can i do this?

答案1

得分: 2

在您的第一个版本中,请使用此 execute 替代...

	/**
	 * 在成功之前或者根据策略决定停止之前,持续执行回调函数,否则将执行恢复回调函数。
	 *
	 * @see RetryOperations#execute(RetryCallback, RecoveryCallback)
	 * @param retryCallback {@link RetryCallback}
	 * @param recoveryCallback {@link RecoveryCallback}
	 * @throws TerminatedRetryException 如果重试被监听器手动终止。
	 */
	@Override
	public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
			RecoveryCallback<T> recoveryCallback) throws E {
		return doExecute(retryCallback, recoveryCallback, null);
	}
template.execute(context -> {
    ...
}, context -> {
    logger.error("Failed to send to ...");
});

如果回调正常退出,则失败将被“恢复”,异常不会被重新抛出。

英文:

In your first version, use this execute instead...

	/**
	 * Keep executing the callback until it either succeeds or the policy dictates that we
	 * stop, in which case the recovery callback will be executed.
	 *
	 * @see RetryOperations#execute(RetryCallback, RecoveryCallback)
	 * @param retryCallback the {@link RetryCallback}
	 * @param recoveryCallback the {@link RecoveryCallback}
	 * @throws TerminatedRetryException if the retry has been manually terminated by a
	 * listener.
	 */
	@Override
	public final &lt;T, E extends Throwable&gt; T execute(RetryCallback&lt;T, E&gt; retryCallback,
			RecoveryCallback&lt;T&gt; recoveryCallback) throws E {
		return doExecute(retryCallback, recoveryCallback, null);
	}
template.execute(context -&gt; {
    ...
}, context -&gt; {
    logger.error(&quot;Failed to send to ...&quot;);
});

If the callback exits normally, the failure is "recovered" and the exception not rethrown.

huangapple
  • 本文由 发表于 2020年10月20日 17:44:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64442564.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定