场景

APP用户上面的反馈功能, 我们需要对用户反馈的数据保存到数据库,然后给产品相关人员发送用户反馈邮箱。

问题

由于邮箱用的是第三方的邮箱服务,有几率出现邮箱服务器连接超时问题,导致连接超时这段期间出现用户反馈的邮箱产品相关的人员查收不到。

解决方案


短信发送失败补偿策略


@Slf4j @Service public class FeedbackServiceImpl implements FeedbackService { @Autowired private JavaMailSender mailSender; @Autowired private Environment env; /** * 邮箱发送失败进入队列 */ private LinkedBlockingQueue sendFailureQueue = new LinkedBlockingQueue<>(1024); private volatile Timer timer ; /** * 邮箱发送线程池 */ ExecutorService executorService = new ThreadPoolExecutor( 1, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024 ), Executors.defaultThreadFactory(), //队列满了后直接丢弃任务 ,同一时间这么多用户反馈,不是核心服务有问题就是服务被恶性攻击 new ThreadPoolExecutor.DiscardPolicy() ); @Override public void sendReportFeedback(TFeedback feedback) { log.info("用户反馈账号account={}", feedback.getAccount()); executorService.execute(()->{ try { sendEmail(feedback); } catch (MessagingException e) { log.error( "用户反馈,邮箱发送异常:{}" , e.getMessage() ); sendFailureQueue.offer( feedback ); }catch(MailException e) { log.error( "用户反馈,邮箱发送异常{}" , e.getMessage() ); sendFailureQueue.offer( feedback ); }finally { //启动重试机制 startTimer(); } }); } /** * 发送邮箱 * @param feedback * @throws MailException * @throws MessagingException */ private void sendEmail(TFeedback feedback) throws MailException , MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = null; helper = new MimeMessageHelper(message, true, "UTF-8"); helper.setFrom(env.getProperty("sys.mail.smtp.from")); helper.setTo(env.getProperty("sys.mail.user.notify").split(",")); StringBuilder sbr = new StringBuilder(); sbr.append("用户邮箱:").append( feedback.getUserEmail() ).append("
"); sbr.append("反馈内容:").append( feedback.getSuggestion() ).append("
"); sbr.append("错误日志:").append( feedback.getLogFilePath() ).append("
"); helper.setText(sbr.toString(), true); helper.setSubject("【xxxx】用户反馈"); mailSender.send(message); log.info("邮件发送成功!!!!"); } /** * 发送失败,重试机制 */ private void startTimer(){ if( sendFailureQueue.size() == 0 ){ log.warn( "FailureQueue is null , return !" ); return; } if( timer != null ){ log.warn( "current timer is running !" ); return; } timer = new Timer(); timer.schedule(new TimerTask(){ public void run() { TFeedback feedback = null; while ( ( feedback = sendFailureQueue.poll( )) != null ){ try { sendEmail( feedback ); } catch (MessagingException e) { log.warn( "反馈邮箱重试出现异常{}" , e.getMessage() ); sendFailureQueue.offer( feedback ); break; }catch ( MailException e ){ log.warn( "反馈邮重试出现异常{}" , e.getMessage() ); sendFailureQueue.offer( feedback ); break; } } if( sendFailureQueue.size() == 0 ){ //所有失败的邮箱都发送完成才清除定时器 timerClose(); } //延时30分钟 , 30分钟后重试 }}, 30*60*1000 , 30*60*1000); } /** * 清除定时器 */ private void timerClose(){ if( timer != null ){ timer.cancel(); timer = null; } } }


备注:

我们对发送失败的反馈邮箱,添加了补偿策略, 这种策略的前提是邮箱服务器只是暂时性连接失败(可能原因是某段时间,用这个服务器的人太多了),最后服务器会恢复正常。 只要我们添加一定的尝试策略,最终只要服务器恢复正常, 反馈信息就发送成功了。


欢迎有不一样的方案,大家一起学习讨论。