Spring 使用 for 循环重试
Spring Retry with for loop
我的 RetryTemplate 配置:
@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;
}
}
调用使用 Retry
:
方法的计划方法
@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");
}
我想使用的方法RetryTemplate
:
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 send.");
}
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;
});
}
}
预期行为:我想为 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 次。我该怎么做?
在您的第一个版本中,请改用 execute
...
/**
* 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 <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 ...");
});
如果回调正常退出,则失败被“恢复”并且不会重新抛出异常。
我的 RetryTemplate 配置:
@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;
}
}
调用使用 Retry
:
@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");
}
我想使用的方法RetryTemplate
:
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 send.");
}
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;
});
}
}
预期行为:我想为 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 次。我该怎么做?
在您的第一个版本中,请改用 execute
...
/**
* 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 <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 ...");
});
如果回调正常退出,则失败被“恢复”并且不会重新抛出异常。