前言:
發(fā)送郵件,肯定是每個公司都會有的基本業(yè)務。很多公司都會選擇把發(fā)送郵件作為一個基礎服務,對外提供接口。直接調用就可發(fā)郵件了。但是我們都知道發(fā)送郵件耗時都比較長。那么今天就介紹下使用Spring boot+eventbus來打造一個簡單郵件服務
規(guī)劃接口列表
發(fā)送郵件的類型準備的有三種
- 發(fā)送普通郵件
- 發(fā)送html郵件
- 發(fā)送圖文郵件
還有一個細節(jié),如果我們同步的取發(fā)送郵件會有兩個問題。
- 接口響應時間比較長
- 遇到并發(fā)的情況,容易導致服務器壓力過大或者郵箱服務封ip
所以我們準備使用隊列來執(zhí)行發(fā)送郵件的操作??梢越鉀Q這個問題。隊列我選用的是Google的eventbus。是一款很輕量的隊列。直接走的內存
準備工作
首先要在pom.xml中引入 需要使用的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- spring-boot-starter-mail :spring-boot提供的發(fā)郵件的maven庫
- guava:google提供的開源庫。里面包含來很多工具
- lombok:可以幫你省去編寫實體類的工具
引入之后,我們還需要配置發(fā)送郵件所需要的必要配置
在application.properties中配置郵箱
spring.mail.host=smtp.mail.me.com //郵箱發(fā)送服務器
spring.mail.port=587//服務器端口
spring.mail.username=xxx6666@icloud.com//發(fā)件人郵箱
spring.mail.password=password//客戶端專用密碼
//如果和我一樣使用的icloud郵箱 還需要下列兩個配置,別的有的郵箱不需要
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
做到這里其實就已經完成了,發(fā)郵件所需要的配置了。但是我們是要用隊列來發(fā)送,所以還需要配置下隊列
@Configuration
public class AsyncEventBusConfig {
//實例化bean,采用單例形式注入容器
@Bean
@Scope("singleton")
public AsyncEventBus asyncEventBus(){
//創(chuàng)建線程池對象
final ThreadPoolExecutor executor=executor();
return new AsyncEventBus(executor);
}
//創(chuàng)建線程池方法
private ThreadPoolExecutor executor(){
return new
ThreadPoolExecutor(2,
2,0L,
TimeUnit.MICROSECONDS,
new LinkedBlockingQueue<>());
}
}
封裝EmailService
準備好了之后,就可以直接來封裝發(fā)送郵件的業(yè)務了。之前有提到我們需要三個接口,同樣的,我們也需要三個service方法
@Service
public class EmailService {
@Autowired
private JavaMailSender javaMailSender;
/**
* 發(fā)件人。這里發(fā)件人一般是同使用的發(fā)件郵箱一致
*/
@Value("${spring.mail.username}")
private String from;
/**
* 發(fā)送文本郵件
* @param to 收件人郵箱地址
* @param subject 主題
* @param content 內容
*/
public void sendTextMail(String to,
String subject,
String content) {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setTo(to);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(content);
simpleMailMessage.setFrom(from);
javaMailSender.send(simpleMailMessage);
}
/**
* 發(fā)送html內容的郵件
* @param to 收件人
* @param htmlContent html內容
* @param subject 主題
* @throws MessagingException
*/
public void sendHtmlMail(String to,
String htmlContent,
String subject) throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
messageHelper.setTo(to);
messageHelper.setSubject(subject);
messageHelper.setFrom(from);
messageHelper.setText(htmlContent, true);
javaMailSender.send(message);
}
/**
* 發(fā)送圖文郵件
* @param to 收件人
* @param imgContent 圖文內容
* @param subject 主題
* @param rscId 資源id
* @param imgPath 資源路徑
* @throws MessagingException
*/
public void sendImgMail(String to,
String imgContent,
String subject,
String rscId,
String imgPath) throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
messageHelper.setTo(to);
messageHelper.setSubject(subject);
messageHelper.setFrom(from);
messageHelper.setText(imgContent, true);
messageHelper.addInline(rscId, new File(imgPath));
javaMailSender.send(message);
}
}
隊列監(jiān)聽
既然封裝好了方法,那么就需要調用。調用的方式,其實就是將接口傳來的數(shù)據傳到隊列里。隊列的消費者接收到了消息就將消息拿來調用發(fā)送郵件的方法
我們首先創(chuàng)建一個消費類,用來接受消息,處理消息。
@Service
public class EventBusListener {
/**
* 引入bean
*/
@Autowired
private AsyncEventBus asyncEventBus;
@Autowired
private EmailService emailService;
/**
* 注冊服務類
*/
@PostConstruct
public void init(){
asyncEventBus.register(this);
}
/**
* 線程安全,消費 文本消息
* @param textEmailDTO
*/
@AllowConcurrentEvents
@Subscribe
public void sendTextMail(TextEmailDTO textEmailDTO){
emailService.sendTextMail(
textEmailDTO.getTo(),
textEmailDTO.getSubject(),
textEmailDTO.getContent()
);
}
/**
* 線程安全 消費 html消息
* @param htmlEmailDTO
*/
@AllowConcurrentEvents
@Subscribe
public void sendHtmlMail(HtmlEmailDTO htmlEmailDTO){
try {
emailService.sendHtmlMail(
htmlEmailDTO.getTo(),
htmlEmailDTO.getHtmlContent(),
htmlEmailDTO.getSubject()
);
} catch (MessagingException e) {
// nothing to do
}
}
/**
* 線程安全 消費 圖文消息
* @param imgEmailDTO
*/
@AllowConcurrentEvents
@Subscribe
public void sendImgMail(ImgEmailDTO imgEmailDTO){
try {
emailService.sendImgMail(
imgEmailDTO.getTo(),
imgEmailDTO.getImgContent(),
imgEmailDTO.getSubject(),
imgEmailDTO.getRscId(),
imgEmailDTO.getImgPath()
);
} catch (MessagingException e) {
// nothing to do
}
}
}
其實eventbus拋消息都是使用的post方法來拋消息。走到不同的方法里面是利用了類的多態(tài),拋入不同的實體類就可以進行區(qū)分了。走進了不同的方法,就調用相應Service方法。
控制器與測試
控制器部分,沒什么好說的,我就貼出圖文的代碼。其余代碼可以在我的碼云上面看
先看眼實體類
@Data
public class ImgEmailDTO implements Serializable {
public ImgEmailDTO() {
}
/**
* 圖片路徑
*/
private String imgPath;
/**
* 資源id
*/
private String rscId;
/**
* 主題
*/
private String subject;
/**
* 圖片正文(同樣可以使用html)
*/
private String imgContent;
/**
* 收件人
*/
private String to;
}
/**
* 發(fā)送圖文郵件
* @param request
* @return
*/
@RequestMapping(value = "/sendImgMail", method = RequestMethod.POST)
public Result<Integer> sendImgMail(@RequestBody Request<ImgEmailDTO> request) {
Result<Integer> result = Result.create();
ImgEmailDTO imgEmailDTO=request.getData();
StringBuilder sb=new StringBuilder();
sb.append(imgEmailDTO.getImgContent());
//cid:資源id。在spring中會自動綁定
sb.append("<img src=\'cid:").append(imgEmailDTO.getRscId()).append("\'></img>");
imgEmailDTO.setImgContent(sb.toString());
asyncEventBus.post(imgEmailDTO);
return result.success(1);
}
圖文要稍微特殊一點,需要拼接下正文內容。然后將實體類中的content替換。最后將實體類拋入隊列。直接返回接口請求。隊列那邊就會排著隊搞定所有的郵件
下面來做個測試

請求很迅速的返回了結果
然后去郵箱中查看結果

好了今天對郵件服務的介紹就寫到這里。知識點并不深奧,主要介紹一個思路。如有不對的地方,請大神指出。謝謝