rabbitmq发送邮件

rabbitmq发送邮件

接收端模块:

依赖:

 
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-amqpartifactId>
    dependency>
    
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-mailartifactId>
    dependency>
    
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
      
      <dependency>
          <groupId>com.xxxxgroupId>
          <artifactId>yeb-serverartifactId>
          <version>0.0.1-SNAPSHOTversion>
      dependency>

配置文件:

server:
  port: 8083
spring:
  mail:
    host: smtp.sina.com
    protocol: smtps
    password: xxxxxx
    default-encoding: utf-8
    username: [email protected]
    port: 465

  rabbitmq:
    host: 192.168.10.100
    port: 5672
    username: guest
    password: guest
#redis
  redis:
    host: 192.168.10.100
    port: 6379
    database: 0
    timeout: 10000ms
    password: root
    lettuce:
      pool:
        max-active: 1024
        max-wait: 10000ms
        max-idle: 200
        min-idle: 5

thymleaf模板:


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>入职欢迎邮件title>
head>
<body>
<h1 align="center">欢迎<p th:text="${name}">p>来到大佬h1>
您所在的部门:<div th:text="${departmentName}">div><br>
您所担任的职位:<div th:text="${jobName}">div><br>
地区:<div th:text="${nationName}">div><br>
<p>大佬软件有限公司©p>
body>
html>

redis配置

@Api("redis配置类")
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> getBean(LettuceConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}
/**
 * @Desc 消息接收者
 * @Author lsh
 * @Create Time 2020/12/22 9:51
 */
@Component
public class MailReceiver {
    private static final Logger LOGGER= LoggerFactory.getLogger(MailReceiver.class);

    @Autowired
    private JavaMailSender javaMailSender;
    @Autowired
    private MailProperties mailProperties;
    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @RabbitListener(queues = MailConstants.MAIL_QUEUE_NAME)
    public void handler(Message message, Channel channel){
        EmpInfo empInfo = ((EmpInfo) message.getPayload());
        MessageHeaders headers = message.getHeaders();
        //消息序号
        long tag = ((long) headers.get(AmqpHeaders.DELIVERY_TAG));
        String msgId = ((String) headers.get("spring_returned_message_correlation"));
        HashOperations hashOperations = redisTemplate.opsForHash();

        try {
            if (hashOperations.entries("mail_log").containsKey(msgId)){
                LOGGER.error("消息已经被消费==========>{}",msgId);
                //手动确认
                channel.basicAck(tag,false);
                return;
            }
            MimeMessage msg=javaMailSender.createMimeMessage();
            MimeMessageHelper helper=new MimeMessageHelper(msg);
            helper.setFrom(mailProperties.getUsername());
            helper.setTo(empInfo.getEmail());
            helper.setSubject("入职欢迎邮件");
            helper.setSentDate(new Date());
            //邮件内容
            Context context=new Context();
            context.setVariable("name",empInfo.getName());
            context.setVariable("departmentName",empInfo.getDepartment().getName());
            context.setVariable("jobName",empInfo.getJoblevel().getName());
            context.setVariable("nationName",empInfo.getNation().getName());
			//发送thymeleaf模板
            String mail = templateEngine.process("mail", context);
            helper.setText(mail,true);
            javaMailSender.send(msg);
            LOGGER.info("邮件发送成功");
            //消息id存入redis中
            hashOperations.put("mail_log",msgId,"ok");
            //手动确认消息
            channel.basicAck(tag,false);
        } catch (Exception e) {
            try {
                /**
                 * 手动确认
                 * requeue 是否重回队列
                 */
                channel.basicNack(tag,false,true);
            } catch (IOException ex) {
                LOGGER.info("邮件发送失败!!"+ex.getMessage());
            }
            LOGGER.info("邮件发送失败!!"+e.getMessage());

        }

    }

}

/**
 * @author lsh
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MailApplication {

    public static void main(String[] args) {
        SpringApplication.run(MailApplication.class, args);
    }

    @Bean
    public Queue queue(){
        return new Queue(MailConstants.MAIL_QUEUE_NAME);
    }

}

发送端:

配置文件:

#rabbitmq配置
  rabbitmq:
    host: 192.168.10.100
    port: 5672
    username: guest
    password: guest
    #消息确认回调
    publisher-confirm-type: correlated
    #消息失败回调
    publisher-returns: true

    #redis
  redis:
    host: 192.168.10.100
    port: 6379
    database: 0
    timeout: 10000ms
    password: root
    lettuce:
      pool:
        max-active: 1024
        max-wait: 10000ms
        max-idle: 200
        min-idle: 5
public class MailConstants {
    //消息投递中
    public static final Integer DELIVERING = 0;
    //消息投成功
    public static final Integer SUCCESS = 1;
    //消息投递失败
    public static final Integer FAILURE = 2;

    //最大重试次数
    public static final Integer MAX_TRY_COUNT = 2;

    //消息超时时间
    public static final Integer MSG_TIMEOUT = 1;

    //队列
    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";
}
           //消息落库
            MailLog mailLog = new MailLog();
            String msgId = UUID.randomUUID().toString();
            mailLog.setMsgId(msgId);
            mailLog.setEid(employee.getId());
            mailLog.setStatus(MailConstants.DELIVERING);
			mailLog.setTryTime(LocalDateTime.now().
                               plusMinutes(MailConstants.MSG_TIMEOUT));
            mailLog.setCount(0);
            mailLog.setExchange(MailConstants.MAIL_EXCHANGE_NAME);
            mailLog.setRouteKey(MailConstants.MAIL_ROUTING_KEY_NAME);
            mailLog.setCreateTime(LocalDateTime.now());
            mailLog.setUpdateTime(LocalDateTime.now());
            AssertUtil.isTrue(!mailLogService.save(mailLog) ,"落库失败");;
            EmpInfo empInfo = employeeService.queryEmpInfoById(employee.getId());
            //发送rabbitmq信息
            rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME,
                                          MailConstants.MAIL_ROUTING_KEY_NAME 
                                          ,empInfo,new CorrelationData(msgId));

rabbitmq配置文件:消息回调

@Component
public class RabbitMQConfig {
    private static final Logger LOGGER= LoggerFactory.getLogger(RabbitMQConfig.class);

    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    @Autowired
    private IMailLogService mailLogService;

    @Bean
    public RabbitTemplate rabbitTemplate(){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 消息确认回调
             * @param data 消息唯一标识
             * @param ack  确认结果
             * @param cause  失败原因
             */
            @Override
            public void confirm(CorrelationData data, boolean ack, String cause) {
                String msgId = data.getId();
                if (ack){
                    LOGGER.info("{}======>消息发送成功",msgId);
                    mailLogService.update(new UpdateWrapper<MailLog>()
                            .set("status",MailConstants.SUCCESS)
                            .eq("msgId",msgId));
                }else{
                    LOGGER.error("{}======>消息发送失败",msgId);
                }
            }
        });
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 消息失败回调
             * @param message  消息主题
             * @param replyCode  响应码
             * @param replyText  响应描述
             * @param exchange  交换机
             * @param routingKey  路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                LOGGER.error("{}======>消息发送queue时失败",message.getBody());
            }
        });

        return rabbitTemplate;
    }


    @Bean
    public Queue queue(){
        return new Queue(MailConstants.MAIL_QUEUE_NAME);
    }
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(MailConstants.MAIL_EXCHANGE_NAME);
    }
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with(MailConstants.MAIL_ROUTING_KEY_NAME);
    }
}

定时任务:

@Component
public class MailTask {
    @Autowired
    private IMailLogService mailLogService;
    @Autowired
    private EmployeeServiceImpl employeeService;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Scheduled(cron = "0/10 * * * * ?")
    public void mailTask(){
        List<MailLog> list = mailLogService.list(new QueryWrapper<MailLog>()
                .eq("status", MailConstants.DELIVERING)
                .lt("tryTime", LocalDateTime.now()));
        //遍历要去发的信息,并且重试的时间到了
        list.forEach(mailLog -> {
            String msgId = mailLog.getMsgId();
            //重试次数超过最大次数
            if (mailLog.getCount()>= MailConstants.MAX_TRY_COUNT) {
                mailLogService.update(new UpdateWrapper<MailLog>()
                        .eq("msgId",msgId)
                        .set("status",MailConstants.FAILURE)
                        .set("updateTime",LocalDateTime.now()));
            }
            mailLogService.update(new UpdateWrapper<MailLog>()
                    .eq("msgId",msgId)
                    .set("count",mailLog.getCount()+1)
                    .set("updateTime",LocalDateTime.now())
                    .set("tryTime",LocalDateTime.now().plusMinutes(MailConstants.MSG_TIMEOUT)));
            EmpInfo empInfo = employeeService.queryEmpInfoById(mailLog.getEid());
            //发送信息
            rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME,MailConstants.MAIL_ROUTING_KEY_NAME
                    ,empInfo,new CorrelationData(msgId));
        });
    }
}

发送端可靠性发送:

​ 使用消息落库的方式,将消息保存到数据库中,然后使用消息回调的机制,改变消息的状态,然后再使用定时任务,将需要重新发的信息进行重发,并改变其重发次数。

接收端幂等性判断:

​ 接收信息的时候得到msgId,发送成功时,将msgId存入redis中,在此接收是需要判断该信息是否在redis中,如果在,则将该信息丢弃,或者重新回到队列

手动确认:

​ 在逻辑代码执行后,在回复ack给消息中间件,保证了消息的正确消费

自动确认:

​ 不建议使用,接受一个消息,自动向消息中间件确认了这个消息被消费了,而现实中就有可能消费者并没有正确消费,或者后续处理有异常导致此消息消费失败

你可能感兴趣的:(rabbitmq,队列)