英文:
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("${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 method which invokes method that uses 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");
}
Method in which i want to use 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;
});
}
}
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 = "${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");
}
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 <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 ...");
});
If the callback exits normally, the failure is "recovered" and the exception not rethrown.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论