Java发送邮件过滤无效地址的方式

public void postMail(String recipients[], String subject, String message, String from) throws MessagingException
	{
		

		Properties props = new Properties();
		props.put("mail.transport.protocol", "smtp");
		props.put("mail.smtp.host", this.smtpHostName);
		props.put("mail.smtp.port", this.smtpPort);
		props.put("mail.smtp.auth", "false");
		props.put("mail.smtp.connectiontimeout", this.connectionTimeout);
		props.put("mail.smtp.timeout", this.socketIOTimeout);
        //创建Session实例对象
		Session session = Session.getDefaultInstance(props, null);

		session.setDebug(false);
        //创建MimeMessage实例对象
		Message msg = new MimeMessage(session);

		InternetAddress addressFrom = new InternetAddress(from);
		//设置发件人地址
		msg.setFrom(addressFrom);

		InternetAddress[] addressTo = new InternetAddress[recipients.length];
		for (int i = 0; i < recipients.length; i++)
		{
			addressTo[i] = new InternetAddress(recipients[i]);
		}
		//设置收件人地址
		msg.setRecipients(Message.RecipientType.TO, addressTo);
        //设置邮件主题
		msg.setSubject(subject);
		//设置message为邮件正文,指定MINI类型为text/plain类型,并制定字符编码为KSC5601
		msg.setContent(message, "text/plain;charset=KSC5601");
		//发送邮件
		try {
			Transport.send(msg);
		}catch(SendFailedException  e) {    
            // 打印无效地址
			Address[] invalid = e.getInvalidAddresses();
			if (invalid != null) {
				System.out.println("unsend mail: invalid address");
				for (int i = 0; i < invalid.length; i++) {
					System.out.println("invalid address:" + invalid[i]);
				}
			}
            // 重新组装邮箱地址
			Address[] validAddresses = e.getValidUnsentAddresses();
			if (validAddresses != null) {
				String[] toUsers = new String[validAddresses.length];
				for (int i = 0; i < validAddresses.length; i++) {
					toUsers[i] = validAddresses[i].toString();
				}
				postMail(toUsers, subject, message, from);
			}

		} 
		catch (MessagingException me) {
			me.printStackTrace();
			System.out.println(me.getMessage());
		}
		

	}

需要在catch里捕获发送失败的异常,SendFailedException,该异常类会获取有效地址和无效地址,将有效地址重新发送就可以,通过递归可实现过滤无效地址

<think>嗯,用户问的是Java邮件时如何处理发送失败和重复发送的问题。首先,我得回忆一下Java发送邮件的基本流程,通常是用JavaMail API或者Spring框架的邮件支持。发送失败可能有很多原因,比如网络问题、服务器配置错误、认证失败等等。而重复发送可能是因为程序在失败后重试,但没做好幂等性控制,导致同一封邮件被多次发送。 首先,发送失败的处理。用户可能需要知道如何捕获异常,然后进行重试。比如,使用try-catch块来捕捉MessagingException,然后设置重试次数。过,重试的时候需要注意间隔时间,比如指数退避策略,避免频繁重试导致服务器压力过大。另外,可能需要记录失败的邮件,方便后续排查问题或者手动处理。比如,可以设计一个重试机制,当捕获到异常时,将邮件信息存入数据库或者队列,然后定时任务去处理这些失败记录。 然后是重复发送的问题。这可能是因为在重试过程中,同一封邮件被多次尝试发送,尤其是在网络波动的情况下,客户端可能认为发送失败,但服务器其实已经处理了请求。这时候需要考虑幂等性,确保即使多次发送请求,也会导致重复邮件。比如,在邮件中添加唯一标识符(比如UUID),在服务器端记录已处理的邮件ID,如果发现重复ID则再处理。或者在业务层面,比如用户触发发送邮件操作时,生成一个唯一的token,每次发送前检查是否已经存在,避免重复提交。 另外,可能需要异步处理邮件发送,避免阻塞主线程,同时提高系统的响应速度。比如使用线程池或者消息队列(如RabbitMQ、Kafka)来异步发送邮件,这样即使某次发送失败,消息队列本身的重试机制也能帮助处理。过,消息队列的使用需要确保消息的持久化,防止系统崩溃导致消息丢失。 还有,用户可能没有提到但需要考虑的是邮件服务器的配置和限制。比如,SMTP服务器可能有发送频率限制,超过限制会被暂时封禁,所以程序中需要做限流处理。同时,监控和日志记录也很重要,记录每次发送的详细信息,包括时间、收件人、状态等,方便后续分析和排查问题。 再想一下,是否需要使用事务来确保邮件发送和其他业务操作的一致性?比如,用户注册成功后发送邮件,如果邮件发送失败,是否需要回滚之前的注册操作?过这在大多数情况下可能太现实,因为邮件发送属于外部系统,无法保证事务的一致性。这时候可能需要补偿机制,比如注册成功但邮件发送失败时,记录状态,后续补发或者提示用户重新发送。 还有,关于邮件内容的校验,比如检查收件人地址是否合法,避免因为格式错误导致发送失败。可以在发送前做一次格式校验,减少无效请求。 总结下来,处理发送失败需要重试机制、异常处理、日志记录和监控;防止重复发送则需要幂等性控制、唯一标识符、异步处理和消息队列等。同时,合理配置邮件服务器,遵守发送频率限制,确保系统的稳定性和可靠性。</think>在Java中处理邮件发送失败和重复问题需要从以下几个方面系统化考虑,以下是具体解决方案及实现思路: --- ### 一、发送失败处理机制 #### 1. 异常捕获与重试 ```java int maxRetries = 3; int retryDelay = 5000; // 毫秒 for (int i = 0; i <= maxRetries; i++) { try { Transport.send(message); break; // 发送成功则退出 } catch (MessagingException e) { if (i == maxRetries) { log.error("邮件发送失败(已达最大重试次数)", e); // 持久化到数据库或消息队列 saveFailedEmailToDB(message); break; } log.warn("发送失败,第{}次重试...", i+1); Thread.sleep(retryDelay * (i+1)); // 递增延迟 } } ``` #### 2. 失败邮件持久化 设计数据库表存储失败记录: ```sql CREATE TABLE failed_emails ( id VARCHAR(36) PRIMARY KEY, recipient VARCHAR(255), content TEXT, error_msg TEXT, retry_count INT DEFAULT 0, created_at DATETIME ); ``` #### 3. 定时补偿任务 ```java @Scheduled(fixedDelay = 3600000) // 每小时执行 public void retryFailedEmails() { List<FailedEmail> emails = fetchFailedEmailsFromDB(); emails.forEach(email -> { if (email.getRetryCount() < 5) { resendEmail(email); updateRetryCount(email.getId()); } else { markAsDeadLetter(email.getId()); } }); } ``` --- ### 二、防止重复发送方案 #### 1. 幂等性控制(推荐方案) ```java // 生成唯一ID(可结合业务参数) String idempotentKey = UUID.randomUUID().toString(); // 发送前校验Redis if (!redis.setnx("email:" + idempotentKey, "1")) { throw new RuntimeException("重复请求"); } // 设置过期时间(例如24小时) redis.expire("email:" + idempotentKey, 86400); // 发送邮件逻辑... ``` #### 2. 业务层面防重 ```java // 用户触发操作时生成防重令牌 String token = UUID.randomUUID().toString(); redis.setex("user:123:email_token", 300, token); // 5分钟有效 // 发送时校验令牌 if (!validateToken(userId, token)) { throw new IllegalStateException("请勿重复提交"); } ``` --- ### 三、增强可靠性措施 #### 1. 异步发送架构 ```java // 使用线程池(建议配置队列容量和拒绝策略) private static final ExecutorService emailExecutor = new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); public void sendEmailAsync(EmailMessage message) { emailExecutor.submit(() -> { sendEmailWithRetry(message); }); } ``` #### 2. 邮件服务器熔断 ```java // 使用Resilience4j实现熔断 CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("emailService"); Supplier<EmailResult> supplier = () -> { try { Transport.send(message); return EmailResult.SUCCESS; } catch (MessagingException e) { throw new RuntimeException(e); } }; EmailResult result = circuitBreaker.executeSupplier(supplier); ``` --- ### 四、监控与预警 1. 关键指标监控: - 发送成功率(成功数/请求总数) - 平均重试次数 - SMTP响应时间 - 死信邮件数量 2. 预警规则示例: ```java if (errorRate > 20% || avgRetryCount > 2) { sendAlert("邮件服务异常:当前错误率" + errorRate); } ``` --- ### 五、最佳实践建议 1. 邮件内容规范: - 收件人地址格式预校验 - 内容长度限制(避免超过SMTP服务器限制) - 敏感词过滤 2. 服务器配置: ```properties # SMTP连接池配置 mail.smtp.connectiontimeout=5000 mail.smtp.timeout=10000 mail.smtp.writetimeout=10000 mail.smtp.ssl.checkserveridentity=true ``` 3. 灰度策略: - 新IP地址发送到测试邮箱 - 逐步提升发送量(每小时增加20%) - 监控退信率(超过5%触发警报) --- 通过以上方案的综合实施,可有效解决以下典型问题场景: | 问题场景 | 解决方案 | |-----------------------|---------------------------| | 网络闪断导致发送失败 | 重试机制 + 异步队列 | | 用户重复点击发送按钮 | 幂等性控制 + 前端防重 | | SMTP服务器过载拒绝服务 | 熔断机制 + 连接池管理 | | 邮件内容违规被拦截 | 内容预校验 + 人工审核队列 | | 海外服务器延迟高 | 分级超时设置 + 区域化部署 | 实际生产环境中建议结合具体业务需求,选择适合的解决方案组合使用,通过压力测试验证系统承载能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值