前言

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. Java多线程学习——任务定时调度

    Timer 本身就是一个线程,最主要的方法就是schedule(). schedule()的参数介绍: schedule(TimerTask task, long delay) //延迟delay毫秒 ...

  2. (3.5)常用知识-NULL与零长度、字符串尾部填充空格

    概述:NULL与零长度是不同的,NULL表示数据未知或不可用,它是与零(数值或2进制).零长度字符串不 同的一种值,也可以理解为一种状态. 即可以理解为:所有的变量都有2种状态,一种有值,一种为NUL ...

  3. 部署CM集群首次运行报错:Formatting the name directories of the current NameNode.

    1. 报错提示 Formatting the name directories of the current NameNode. If the name directories are not emp ...

  4. 【五一qbxt】day3 动态规划

    动态规划 引例: 斐波那契数列: 边界条件:f0=0: f1=1: 能够直接被求出值的状态 不需要计算其他斐波那契数列的值直接可以得到结果: 转移方程:fn=fn-1+fn-2如何用已有状态求出未知状 ...

  5. linux的进程间通信概述

    一 进程间通信 1.1. linux内核提供多种进程间通信机制 a. 无名管道和有名管道 b. SystemV IPC:信号量.消息队列.共享内存 c. Socket域套接字 d. 信号 1.2. 无 ...

  6. 通过编写串口助手工具学习MFC过程——(一)工程新建

    通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...

  7. RabbitMQ交换器Exchange介绍与实践

    RabbitMQ交换器Exchange介绍与实践 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器Exchang ...

  8. input 限制 中文输入

    ime-mode:disabled是什么? 解决: 1.     ime-mode版本:IE5+专有属性 继承性:无    语法:     ime-mode : auto | active | ina ...

  9. linux 源码安装postgresql

    下载源码包 --安装所需要的系统软件包 yum groupinstall -y "Development tools" yum install -y bison flex read ...

  10. CentOS7 安装 Mysql5.6.40

    CentOS7.5二进制安装MySQL-5.6.40 安装之后登陆不上,mysql.user 表是空的时: Mysql User表为空 mysql创建用户报错ERROR 1364 (HY000): F ...