前言

Linux安装RabbitMQ:https://www.cnblogs.com/jxd283465/p/11975094.html

SpringBoot整合RabbitMQ:https://www.cnblogs.com/jxd283465/p/11975136.html

流程

代码

数据库表

CREATE TABLE `msg_log` (
`msg_id` varchar(255) NOT NULL DEFAULT '' COMMENT '消息唯一标识',
`msg` text COMMENT '消息体, json格式化',
`exchange` varchar(255) NOT NULL DEFAULT '' COMMENT '交换机',
`routing_key` varchar(255) NOT NULL DEFAULT '' COMMENT '路由键',
`status` int(11) NOT NULL DEFAULT '' COMMENT '状态: 0投递中 1投递成功 2投递失败 3已消费',
`try_count` int(11) NOT NULL DEFAULT '' COMMENT '重试次数',
`next_try_time` datetime DEFAULT NULL COMMENT '下一次重试时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`msg_id`),
UNIQUE KEY `unq_msg_id` (`msg_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息投递日志';

邮件发送类

package cc.mrbird.febs.common.utils;
/**
* 邮件发送工具类。
* 以下邮件中的配置参数,请在实际环境中,根据需要采取合适的配置方式。
* 发送邮件依赖 com.sun.mail(1.6.1) 包、javax.mail(1.5.0-b01) 包。
* 如果使用 Idea 运行,请将这两个包(可以直接到Maven目录下面去找)添加到项目的 Libraries 里面(快捷键:Ctrl + Alt + Shift + S)
*
* @author Zebe
*/
public class SendEmailUtil { /**
* 发件人别名(可以为空)
*/
private final static String fromAliasName = "***"; /**
* 登录用户名
*/
private String ACCOUNT; /**
* 登录密码
*/
private String PASSWORD; /**
* 邮件服务器地址
*/
//QQ企业邮箱:smtp.exmail.qq.com
//网易企业邮箱:smtphz.qiye.163.com
private String HOST; /**
* 发信端口
*/
//QQ企业邮箱:465
//网易企业邮箱:994
private String PORT; /**
* 发信协议
*/
private final static String PROTOCOL = "ssl"; /**
* 收件人
*/
private String to; /**
* 收件人名称
*/
private String toName; /**
* 主题
*/
private String subject; /**
* 内容
*/
private String content; /**
* 附件列表(可以为空)
*/
private List<String> attachFileList; /**
* 构造器
*
* @param attachFileList 附件列表
*/
public SendEmailUtil(MailTemplate mailTemplate, List<String> attachFileList) {
this.to = mailTemplate.getTo();
this.toName = mailTemplate.getToName();
this.subject = mailTemplate.getSubject();
this.content = mailTemplate.getContent();
this.attachFileList = attachFileList;
this.ACCOUNT = mailTemplate.getAccount();
this.PASSWORD = mailTemplate.getPassword();
switch (mailTemplate.getSendType()) {
case "qq":
this.HOST = "smtp.exmail.qq.com";
this.PORT = "465";
break;
case "163":
this.HOST = "smtp.ym.163.com";
this.PORT = "994";
break;
}
} /**
* 认证信息
*/
static class MyAuthenticator extends Authenticator { /**
* 用户名
*/
String username = null; /**
* 密码
*/
String password = null; /**
* 构造器
*
* @param username 用户名
* @param password 密码
*/
public MyAuthenticator(String username, String password) {
this.username = username;
this.password = password;
} @Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
} /**
* 发送邮件
*/
public boolean send() {
// 设置邮件属性
Properties prop = new Properties();
prop.setProperty("mail.transport.protocol", PROTOCOL);
prop.setProperty("mail.smtp.host", HOST);
prop.setProperty("mail.smtp.port", PORT);
prop.setProperty("mail.smtp.auth", "true");
MailSSLSocketFactory sslSocketFactory = null;
try {
sslSocketFactory = new MailSSLSocketFactory();
sslSocketFactory.setTrustAllHosts(true);
} catch (GeneralSecurityException e1) {
e1.printStackTrace();
}
if (sslSocketFactory == null) {
System.err.println("开启 MailSSLSocketFactory 失败");
} else {
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sslSocketFactory);
// 创建邮件会话(注意,如果要在一个进程中切换多个邮箱账号发信,应该用 Session.getInstance)
Session session = Session.getDefaultInstance(prop, new MyAuthenticator(ACCOUNT, PASSWORD));
// 开启调试模式(生产环境中请不要开启此项)
session.setDebug(true);
try {
MimeMessage mimeMessage = new MimeMessage(session);
// 设置发件人别名(如果未设置别名就默认为发件人邮箱)
mimeMessage.setFrom(new InternetAddress(ACCOUNT, fromAliasName));
// 设置主题和收件人、发信时间等信息
mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(to, toName));
mimeMessage.setSubject(subject);
mimeMessage.setSentDate(new Date());
// 如果有附件信息,则添加附件
if (!attachFileList.isEmpty()) {
Multipart multipart = new MimeMultipart();
MimeBodyPart body = new MimeBodyPart();
body.setContent(content, "text/html; charset=UTF-8");
multipart.addBodyPart(body);
// 添加所有附件(添加时判断文件是否存在)
for (String filePath : attachFileList) {
if (Files.exists(Paths.get(filePath))) {
MimeBodyPart tempBodyPart = new MimeBodyPart();
tempBodyPart.attachFile(filePath);
multipart.addBodyPart(tempBodyPart);
}
}
mimeMessage.setContent(multipart);
} else {
Multipart multipart = new MimeMultipart();
MimeBodyPart body = new MimeBodyPart();
body.setContent(content, "text/html; charset=UTF-8");
multipart.addBodyPart(body);
mimeMessage.setContent(multipart);
//mimeMessage.setText(content);
}
// 开始发信
mimeMessage.saveChanges();
Transport.send(mimeMessage);
return true;
} catch (MessagingException | IOException e) {
e.printStackTrace();
return false;
}
}
return false;
}
}

邮件模板

@Data
@NoArgsConstructor
public class MailTemplate implements Serializable { private String msgId;
/**
* 收件人
*/
private String to; /**
* 收件人名称
*/
private String toName; /**
* 主题
*/
private String subject; /**
* 内容
*/
private String content; /**
* 附件列表
*/
private List<String> attachFileList; /**
* 邮箱账号
*/
private String account; /**
* 邮箱密码
*/
private String password; /**
* 邮箱类型
*/
private String sendType; /**
* 构造器
*
* @param to 收件人
* @param subject 主题
* @param content 内容
*/
public MailTemplate(String account, String password, String sendType, String to, String toName, String subject, String content) {
this.account = account;
this.password = password;
this.sendType = sendType;
this.to = to;
this.toName = toName;
this.subject = subject;
this.content = content;
} /**
* 构造器
*
* @param to 收件人
* @param subject 主题
* @param content 内容
* @param attachFileList 附件列表
*/
public MailTemplate(String account, String password, String sendType, String to, String toName, String subject, String content, List<String> attachFileList) {
this(account, password, sendType, to, toName, subject, content);
this.attachFileList = attachFileList;
}
}

rabbit mq配置类

@Configuration
@Slf4j
public class RabbitConfig { // 发送邮件
public static final String MAIL_QUEUE_NAME = "mail.queue";
public static final String MAIL_EXCHANGE_NAME = "mail.exchange";
public static final String MAIL_ROUTING_KEY_NAME = "mail.routing.key";
public final static Integer MAIL_DELIVER_SUCCESS = 1;
public final static Integer MAIL_DELIVER_FAIL = 2;
public final static Integer MAIL_CONSUMED_SUCCESS = 3;
public static boolean ENABLE_SCHEDULED = false;
private final CachingConnectionFactory connectionFactory; @Autowired
private IMsgLogService iMsgLogService; public RabbitConfig(CachingConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
} @Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(converter()); // 消息是否成功发送到Exchange
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
log.info("消息成功发送到Exchange");
String msgId = correlationData.getId();
UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("msg_id",msgId);
MsgLog msgLog = new MsgLog();
msgLog.setStatus(MAIL_DELIVER_SUCCESS);
iMsgLogService.update(msgLog, updateWrapper);
} else {
log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause);
}
}); // 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调
rabbitTemplate.setMandatory(true);
// 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);
}); return rabbitTemplate;
} @Bean
public Jackson2JsonMessageConverter converter() {
return new Jackson2JsonMessageConverter();
} @Bean
public Queue mailQueue() {
return new Queue(MAIL_QUEUE_NAME, true);
} @Bean
public DirectExchange mailExchange() {
return new DirectExchange(MAIL_EXCHANGE_NAME, true, false);
} @Bean
public Binding mailBinding() {
return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(MAIL_ROUTING_KEY_NAME);
} }

生产者

@RestController
@RequestMapping("mail")
public class MailController { @Autowired
private IMsgLogService iMsgLogService;
@Autowired
private RabbitTemplate rabbitTemplate; @PostMapping("/test")
public void test(String account, String password, String sendType) {
try {
for (int j = 1; j <= 3; j++) {
for (int i = 1; i <= 15; i++) {
for (int num = 1; num <= 73; num++) {
// 设置发信参数
final String toName = "我是" + num + "号";
final String to = "test" + num + "@forexgwg.com";
String subject = num + " 第" + num + "次发送测试邮件标题";
final String content = "<p style='color:red'>这是邮件内容正文。</p></br>";
MailTemplate mailTemplate = new MailTemplate();
String msgId = UUID.randomUUID().toString();
mailTemplate.setMsgId(msgId);
mailTemplate.setAccount(account);
mailTemplate.setPassword(password);
mailTemplate.setSendType(sendType);
mailTemplate.setToName(toName);
mailTemplate.setTo(to);
mailTemplate.setSubject(subject);
mailTemplate.setContent(content);
mailTemplate.setAttachFileList(new ArrayList<>()); MsgLog msgLog = new MsgLog(msgId, JSON.toJSONString(mailTemplate), RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, LocalDateTime.now());
iMsgLogService.save(msgLog);
CorrelationData correlationData = new CorrelationData(msgId);
Thread.sleep(1000);
rabbitTemplate.convertAndSend(RabbitConfig.MAIL_EXCHANGE_NAME, RabbitConfig.MAIL_ROUTING_KEY_NAME, JSON.toJSONString(mailTemplate), correlationData);// 发送消息
}
}
}
RabbitConfig.ENABLE_SCHEDULED = true;
} catch (Exception e) {
System.out.println("错误: " + e);
}
}
}

消费者

@Component
@Slf4j
public class MailConsumer { @Autowired
private IMsgLogService iMsgLogService; @RabbitListener(queues = RabbitConfig.MAIL_QUEUE_NAME)
public void consume(Message message, Channel channel) throws IOException {
String msg = new String(message.getBody());
msg = msg.replaceAll("\\\\", "");
msg = msg.substring(1, msg.length() - 1);
MailTemplate mailTemplate = JSON.parseObject(msg, MailTemplate.class);
log.info("收到消息: {}", mailTemplate.toString()); String msgId = mailTemplate.getMsgId();
QueryWrapper<MsgLog> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("msg_id", msgId);
UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("msg_id", msgId);
MsgLog msgLog = iMsgLogService.getOne(queryWrapper); // 消费幂等性
if (null == msgLog || msgLog.getStatus().equals(RabbitConfig.MAIL_CONSUMED_SUCCESS)) {
log.info("重复消费, msgId: {}", msgId);
return;
} msgLog.setStatus(3);
msgLog.setUpdateTime(LocalDateTime.now());
iMsgLogService.update(msgLog, updateWrapper); MessageProperties properties = message.getMessageProperties();
long tag = properties.getDeliveryTag(); boolean success = new SendEmailUtil(mailTemplate, new ArrayList<>()).send();
if (success) {
msgLog.setStatus(RabbitConfig.MAIL_CONSUMED_SUCCESS);
msgLog.setUpdateTime(LocalDateTime.now());
iMsgLogService.update(msgLog, updateWrapper);
log.info("消费成功!");
channel.basicAck(tag, false);// 消费确认
} else {
channel.basicNack(tag, false, true);
}
}
}

重新发送

@Component
@Slf4j
public class ResendMsg { @Autowired
private IMsgLogService iMsgLogService; @Autowired
private RabbitTemplate rabbitTemplate; // 最大投递次数
private static final int MAX_TRY_COUNT = 3; /**
* 每30s拉取投递失败的消息, 重新投递
*/
@Scheduled(cron = "0 0/1 * * * ?")
public void resend() {
log.info("开始执行定时任务(重新投递消息)");
UpdateWrapper<MsgLog> updateWrapper = new UpdateWrapper<>();
QueryWrapper<MsgLog> queryWrapper = new QueryWrapper<>();
queryWrapper.ne("status", RabbitConfig.MAIL_DELIVER_FAIL).ne("status", RabbitConfig.MAIL_CONSUMED_SUCCESS); List<MsgLog> msgLogs = iMsgLogService.list(queryWrapper);
if (msgLogs.size() == 0){
RabbitConfig.ENABLE_SCHEDULED = false;
}
msgLogs.forEach(msgLog -> {
String msgId = msgLog.getMsgId();
updateWrapper.eq("msg_id", msgId);
if (msgLog.getTryCount() >= MAX_TRY_COUNT) {
msgLog.setStatus(RabbitConfig.MAIL_DELIVER_FAIL);
msgLog.setUpdateTime(LocalDateTime.now());
iMsgLogService.update(msgLog, updateWrapper);
log.info("超过最大重试次数, 消息投递失败, msgId: {}", msgId);
} else {
msgLog.setTryCount(msgLog.getTryCount() + 1);
msgLog.setUpdateTime(LocalDateTime.now());
msgLog.setNextTryTime(LocalDateTime.now().plusSeconds(60));
iMsgLogService.update(msgLog, updateWrapper);// 投递次数+1 CorrelationData correlationData = new CorrelationData(msgId);
rabbitTemplate.convertAndSend(msgLog.getExchange(), msgLog.getRoutingKey(), msgLog.getMsg(), correlationData);// 重新投递
log.info("第 " + (msgLog.getTryCount() + 1) + " 次重新投递消息");
}
});
log.info("定时任务执行结束(重新投递消息)");
} }

使用不同的兩個帳戶发送email时,第一个账户可以发送成功,但到第二个账户的时候就报出了501 mail from address must be same as authorization user的错误。

Session session = Session.getDefaultInstance(props, auth);
以上改成
Session session = Session.getInstance(props, auth);

【JavaMail】JavaMail整合RabbitMq发送邮件案例的更多相关文章

  1. JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件

    一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封简单的邮件包含邮件头和邮件体两个部分,邮件头和邮件体之间使用空行分隔. 邮件头包含的内容有: from字段 ...

  2. (转载)JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件

    博客源地址:http://www.cnblogs.com/xdp-gacl/p/4216311.html 一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封 ...

  3. spring利用javamail,quartz定时发送邮件 <转>

    原文地址:spring利用javamail,quartz定时发送邮件 <转>作者:物是人非 spring提供的定时发送邮件功能,下面是一个简单的例子以供大家参考,首先从spring配置文件 ...

  4. 使用JavaMail创建邮件和发送邮件

    参考https://www.cnblogs.com/xdp-gacl/p/4216311.html,写的真好,知识在于分享,备份留着看 一.RFC882文档简单说明 RFC882文档规定了如何编写一封 ...

  5. JavaMail实现邮箱之间发送邮件功能

    package com.minstone.message.util; import java.util.Date; import java.util.Properties; import javax. ...

  6. 一篇学习完rabbitmq基础知识,springboot整合rabbitmq

    一   rabbitmq 介绍 MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced MessageQueue 高级消息队列协议 ...

  7. Spring Boot 整合 rabbitmq

    一.消息中间件的应用场景 异步处理 场景:用户注册,信息写入数据库后,需要给用户发送注册成功的邮件,再发送注册成功的邮件. 1.同步调用:注册成功后,顺序执行发送邮件方法,发送短信方法,最后响应用户 ...

  8. java框架之SpringBoot(12)-消息及整合RabbitMQ

    前言 概述 大多数应用中,可通过消息服务中间件来提升系统异步通信.扩展解耦的能力. 消息服务中两个重要概念:消息代理(message broker)和目的地(destination).当消息发送者发送 ...

  9. Spring Boot (5) 整合 RabbitMQ

    一.前言 RabbitMQ是实现了AMQP(高级消息队列协议)的开源消息中间件,RabbitMQ服务器是用Erlang(面向并发的编程语言)编写的. RabbitMQ官网下载地址:https://ww ...

随机推荐

  1. 【ABAP系列】SAP ABAP DOI展示EXCEL或WORD

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP DOI展示EX ...

  2. Java回调机制的理解

    用一句话讲明回调机制就是,在A类里面拥有一个类B的对象,调用B类的某个方法并把自身引用传入,在B类的这个方法里面又通过传进来的A的引用来调用A类的某个方法(这个最后调用的A类的方法就叫做回调方法). ...

  3. AutoML文献阅读

    逐步会更新阅读过的AutoML文献(其实是NAS),以及自己的一些思考 Progressive Neural Architecture Search,2018ECCV的文章: 目的是:Speed up ...

  4. 《React+Redux前端开发实战》笔记1:不涉及React项目构建的Hello World案例

    本小节实现一个不涉及项目构建的Hello World. [React的第一个Hello World网页] 源码地址:https://jsfiddle.net/allan91/2h1sf0ky/8/ & ...

  5. python调用java代码 java虚拟机(jvm)

    1.新建com文件夹,在里面新建 fibnq.java package com; public class fibnq { public fibnq(){} public int fb(int n){ ...

  6. 【Linux开发】V4L2应用程序框架

    V4L2应用程序框架 V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移.更好地了解V4L2先从应用入手,然后再深入到内核中结合物理 ...

  7. index.html(xpath素材)

    <bookstore> <title>新华书店</title> <book href="http://www.langlang2017.com/&q ...

  8. git关联github远程仓库的问题

    git关联github远程仓库的时候,报fatal: remote origin already exists. 导致这个问题原因可能是之前关联的时候关联错了,再次关联就不行了. 解决办法是: 1.将 ...

  9. 第二周JAVA总结

    学海无涯,在学习这件事情上得用点心了

  10. 使用certbot 为nginx或openresty启用https

    certbot 使用letsencrypt 生成免费https证书 以下内容网上整理,原文地址 https://segmentfault.com/a/1190000005797776 -------- ...