使用者註冊後發送歡迎信若同步處理,會阻塞回應、降低吞吐量,且若郵件伺服器暫時不可用會導致註冊失敗。引入 RabbitMQ 將「發送郵件」解耦為非同步任務:Producer 將郵件任務投入佇列即刻返回,Consumer 背景消費並重試發送,大幅提升系統韌性與回應速度。
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<beans:beans xmlns="https://www.springframework.org/schema/rabbit"
xmlns:beans="https://www.springframework.org/schema/beans"
xsi:schemaLocation="https://www.springframework.org/schema/rabbit
https://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 連線工廠,生產環境請改為環境變數 -->
<connection-factory id="connectionFactory"
host="localhost" port="5672" username="everSeeker" password="333" />
<admin connection-factory="connectionFactory" />
<!-- 宣告 Queue,durable=true 確保重啟不遺失 -->
<queue id="userAlertEmailQueue" name="user.alerts.email" durable="true" />
<!-- Direct Exchange:Routing Key 完全匹配 -->
<direct-exchange name="user.alert.email.exchange" durable="true">
<bindings>
<binding queue="user.alerts.email" /> <!-- 預設 routing key = queue name -->
</bindings>
</direct-exchange>
<!-- RabbitTemplate 發送訊息 -->
<template id="rabbitTemplate" connection-factory="connectionFactory" />
<!-- Listener Container + Consumer -->
<beans:bean id="userListener" class="com.example.alerts.UserAlertHandler" />
<listener-container connection-factory="connectionFactory" acknowledge="auto">
<listener ref="userListener" method="handleUserAlertToEmail" queues="userAlertEmailQueue" />
</listener-container>
</beans:beans>
注意:若使用預設 guest/guest 登入會報 ACCESS_REFUSED,請在 RabbitMQ Management UI (https://localhost:15672) 建立具 administrator 權限的用戶並授權虛擬主機。
@Service
public class UserAlertServiceImpl implements UserAlertService {
private final RabbitTemplate rabbitTemplate;
@Autowired
public UserAlertServiceImpl(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
@Override
public void sendUserAlertToEmail(User user) {
// 將 User 物件序列化為訊息發送到 Exchange
rabbitTemplate.convertAndSend("user.alert.email.exchange", "user.alerts.email", user);
}
}
@Component
public class UserAlertHandler {
private final UserMailService mailService;
@Autowired
public UserAlertHandler(UserMailService mailService) {
this.mailService = mailService;
}
public void handleUserAlertToEmail(User user) {
mailService.sendSimpleUserMail(user.getEmail(), user);
}
}
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
@Bean
public MailSender mailSender(Environment env) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(env.getProperty("mail.host"));
sender.setPort(Integer.parseInt(env.getProperty("mail.port")));
sender.setUsername(env.getProperty("mail.username"));
sender.setPassword(env.getProperty("mail.password"));
sender.setDefaultEncoding("UTF-8");
// SSL 設定(Gmail、QQ 郵箱需啟用)
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.port", "465");
sender.setJavaMailProperties(props);
return sender;
}
@Service
public class UserMailServiceImpl implements UserMailService {
private final MailSender mailSender;
@Autowired
public UserMailServiceImpl(MailSender mailSender) {
this.mailSender = mailSender;
}
@Override
public void sendSimpleUserMail(String to, User user) {
SimpleMailMessage msg = new SimpleMailMessage();
msg.setFrom("noreply@example.com");
msg.setTo(to);
msg.setSubject(user.getUsername() + ",歡迎註冊!");
msg.setText("親愛的 " + user.getUsername() + ",感謝您的註冊。");
mailSender.send(msg);
}
}
acknowledge="manual")、佇列與訊息持久化(durable="true")。Jackson2JsonMessageConverter)。