邮件发送兜底邮箱策略 - SMTPSendFailedException: 421 4.4.5 HL:ICC 邮件发送失败重试机制应用

邮件发送兜底邮箱策略 - SMTPSendFailedException: 421 4.4.5 HL:ICC

项目测试和生产环境使用的都是163企业邮箱发送,如果测试和生产定时任务在同一时间会产生如上163的提示报错。
并且增加了重试机制,邮件发送失败后,重试2次,间隔30秒。
服务邮件发送服务优化-增加重试机制和减少并发,测试环境和生产环境的xxlJob执行错开时间,且将测试环境的定时任务暂停。

解决方案:
1.邮件发送失败,增加失败重试发送机制。
2.测试环境的定时任务邮件发送错开时间,暂停发送。减少对线上同一个邮件任务的并发发送的冲突
3.邮件地址不存在或错误的地址,更正。

1.针对邮件列表中邮箱地址不存在的情况(离职人员的邮箱地址不存在),增加了补充兜底的可确定的邮箱地址来接收,避免邮件发送失败而不可知。
javax.mail.SendFailedException: Invalid Addresses 邮件地址错误,兜底发送。
2.成员变量的赋值。 否则都是 list为空
3.邮件发送返回成功和失败标识 @Async 异步不能返回,捕获异常。需要接口的返回值,需要去掉@Async
4.设计思路:
1.两份邮件,邮件收件人的地址配置分开单独配置,为了后续的扩展。可以允许单独配置,比较灵活。
2.两份邮件,数据查询的实体类Entity分开创建,为了后续的扩展。
5.//使用465

final String SSLport="465";
final String sslFactory = "javax.net.ssl.SSLSocketFactory";
props.put("mail.smtp.port", SSLport);
props.put("mail.smtp.socketFactory.class", sslFactory);
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.socketFactory.port", SSLport);

//参考源码(commons-email:1.3.3):org.apache.commons.mail.Email.java
SimpleEmail.java

EmailConstants.java
public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback";
public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = "mail.smtp.socketFactory.class";
public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = "mail.smtp.socketFactory.port";
public static final String MAIL_PORT = "mail.smtp.port";
                
Email.java
protected String sslSmtpPort = "465";
@Deprecated
public static final String MAIL_PORT = EmailConstants.MAIL_PORT;
@Deprecated
public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT;
@Deprecated
public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS;
@Deprecated
public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_FALLBACK;
    
if (isSSLOnConnect())
{
    properties.setProperty(MAIL_PORT, this.sslSmtpPort);
    properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, this.sslSmtpPort);
    properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
    properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false");
}

6.发信频率过高的解决办法——降低频率
可以通过设置线程 或将邮件先存入数据库再设置定时JOB发送的方式实施

参考:Spring异常重试框架Spring Retry 重试机制应用
https://www.cnblogs.com/oktokeep/p/18279179

//伪代码
@Retryable(value = {RetrySendEmailException.class},maxAttempts = 3,backoff = @Backoff(delay=60000, multiplier = 2))
public boolean sendEmailLog(ReqEmail req,boolean isDelay) throws Exception{
        //将本次的请求对象转换为json,做md5,避免重复发送
        
        //如果是重试发送,则需要绕过 重复发送的拦截
        /**
         * 发送失败重试,则无需该拦截判断
         */
        if(req.isRetrySendFlag() == false && emailLogMapper.getEmailLogExists(md5) > 0){
            throw new CommonEmailException(ErrorCode.SEND_SAME_ERROR);
        }
        if(req.isRetrySendFlag()){
            //产生新的md5标识,继承上一次的标识。 重新赋值
            LocalDateTime ldt = LocalDateTime.now();
            String minute = String.valueOf(ldt.getMinute());
            String second = String.valueOf(ldt.getSecond());
            md5 = md5 +"-"+ minute+second;
            entity.setEmailContentMd5(md5);
        }
        
        //发送邮件
        sendFlag = sendEmail();
        
        if(sendFlag == false){
            logger.info("发送异常,重新发送>>>[],retryFlag=[{}]", req.getEmailSubject(),req.isRetrySendFlag());
            //重新赋值,表示需要重试发送邮件。在下一次的执行中,请求对象用应用该新值。
            req.setRetrySendFlag(true);
            throw new RetrySendEmailException("999999","发送异常,重新发送");
        }
}

备注:maxAttempts = 3,backoff = @Backoff(delay=60000, multiplier = 2)
测试下来:执行了2次重试,每次间隔30秒。而不是1分钟。

posted on 2024-11-28 19:07  oktokeep  阅读(130)  评论(1)    收藏  举报