You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring整合RabbitMQ无法发送Gmail邮件问题排查求助

问题

我正在开发一个Spring小型项目,需要将超级管理员设置的默认密码发送至管理员的Gmail邮箱(希望通过RabbitMQ实现,如有其他建议欢迎告知)。目前使用RabbitMQ时,发送至队列的消息处于未确认(unacked)状态后会立即被删除,即使消息到达消费者,也无法发送至目标Gmail邮箱。请问可能的原因是什么?


相关代码

RabbitMQConfig

@Configuration 
public class RabbitMQConfig { 

@Bean 
public Queue emailQueue() {
 return new Queue("emailQueue",false); 
}

@Bean
public TopicExchange emailTopicExchange() {
    return new TopicExchange("emailTopicExchange",true,false);
}

@Bean
public Binding binding(Queue emailQueue, TopicExchange emailTopicExchange) {
    return BindingBuilder.bind(emailQueue).to(emailTopicExchange).with("email.key");
}

@Bean
public MessageConverter jsonMessageConverter() {
    return new Jackson2JsonMessageConverter();
}

@Bean
public ConnectionFactory connectionFactory() {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");
    connectionFactory.setPort(5672);
    return connectionFactory;
}

@Bean
public AmqpTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
    final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMessageConverter(jsonMessageConverter());
    return rabbitTemplate;
}
}

EmailProducer

@Service 
public class EmailProducer { 
private final RabbitTemplate rabbitTemplate;

public EmailProducer(RabbitTemplate rabbitTemplate) {
    this.rabbitTemplate = rabbitTemplate;
}

public void sendEmailMessage(String email, String password) {
    Map<String, String> message = new HashMap<>();
    message.put("email", email);
    message.put("password", password);
    rabbitTemplate.convertAndSend("emailTopicExchange", "email.key", message);
}
}

EmailConsumer

@Service 
public class EmailConsumer {
private final MailService mailService;

public EmailConsumer(MailService mailService) {
    this.mailService = mailService;
}

@RabbitListener(queues = "emailQueue")
public void receiveMessage(Map<String, String> message)  {
    String email = message.get("email");
    String password = message.get("password");
    mailService.sendMail(email, password);
  
}
}

MailService

@FieldDefaults(level = AccessLevel.PRIVATE)
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
public class MailService {
        final  JavaMailSender mailSender;
        public void sendMail(String email,String text) {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(private);
            message.setTo(email);
            message.setText("Salam sizin admin passwordunuz: " + text);
            message.setSubject("Library Admin");
            mailSender.send(message);
        }
}

application.yml

mail:
    host: smtp.gmail.com
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true

    username: private
    port: 587
    password: private

  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

可能的原因分析

1. MailService未被Spring容器管理

MailService仅添加了@FieldDefaults和@RequiredArgsConstructor,缺少@Service或@Component注解,Spring无法创建该类实例。EmailConsumer注入时会直接报错,导致消费者无法正常启动,消息一直处于unacked状态,最终因消费者连接异常断开被RabbitMQ处理删除。

2. 邮件发送抛出未捕获异常,触发消息自动删除

EmailConsumer的receiveMessage方法未做异常捕获,如果mailService.sendMail执行时抛出异常(比如Gmail认证失败、网络问题),Spring AMQP会自动拒绝这条消息。而你的队列是非持久化(new Queue("emailQueue",false)),拒绝后消息会直接被删除,不会重新入队。

3. Gmail SMTP配置错误

  • Gmail现在默认禁止普通密码用于SMTP认证,需要使用应用专用密码(需开启两步验证),否则会被Google拦截,导致mailSender.send抛出异常。
  • MailService中message.setFrom(private)的private需替换为application.yml中配置的mail.username,否则发件人身份不匹配也会导致发送失败。

4. RabbitMQ消息确认机制未合理配置

默认的自动确认模式下,消费者处理异常会触发nack(否定确认),非持久化消息直接被删除。没有配置重试或死信队列机制,导致消息丢失。


修复建议

  1. 给MailService添加@Service注解,让Spring容器管理该类实例。
  2. 调整Gmail认证方式:开启账号两步验证后生成应用专用密码,替换application.yml中的mail.password;同时确保MailService的setFrom值与mail.username一致。
  3. 将队列改为持久化:new Queue("emailQueue",true),配合重试或死信队列避免消息丢失。
  4. 修改消费者方法,添加异常捕获与手动确认逻辑:
@RabbitListener(queues = "emailQueue")
public void receiveMessage(Map<String, String> message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
    try {
        String email = message.get("email");
        String password = message.get("password");
        mailService.sendMail(email, password);
        channel.basicAck(deliveryTag, false); // 手动确认消息处理成功
    } catch (Exception e) {
        channel.basicNack(deliveryTag, false, true); // 拒绝并重新入队,或根据需求配置死信
        e.printStackTrace();
    }
}

其他实现建议

小型项目中如果不需要复杂的消息可靠性保障,也可以使用Spring的@Async注解实现异步发送邮件,无需中间件;如果需要消息不丢失、重试等能力,RabbitMQ配合持久化、死信队列的方案更合适。

内容的提问来源于stack exchange,提问作者Talib Aliyev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:42:05