首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >邮件发送兜底邮箱策略 - SMTPSendFailedException: 421 4.4.5 HL:ICC

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

作者头像
oktokeep
发布于 2024-11-29 07:56:20
发布于 2024-11-29 07:56:20
7730
举报
文章被收录于专栏:第三方工具第三方工具

邮件发送兜底邮箱策略 - 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

代码语言:javascript
复制
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://cloud.tencent.com/developer/article/2455891

代码语言:javascript
复制
//伪代码
@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分钟。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档