RabbitMQ及器高级应用

学习内容:

  1. Rabbitmq的架构
  2. Rabbitmq的消息模型
  3. Rabbitmq交换机类型
  4. Rabbitmq死信队列
  5. Rabbitmq延迟队列

一、Rabbitmq的架构


二、Rabbitmq的消息模型、Rabbitmq交换机类型

Ⅰ.简单队列模式(会使用默认的交换机)

        

         看似是消费者直接将消息发送到队列中,实际上使用了默认的交换机,只有一个消费者,消费队列

        交换机类型:直连交换机

Ⅱ.工作队列模式(会使用默认的交换机)

         存在多个消费者消费队列中的消息,提升消费能力,防止消息堆积

        交换机类型:直连交换机 

Ⅲ.发布订阅模式

        ①广播

        

         消费者将消息通过交换机路由到所有与之绑定的队列中,每个队列中的消费者消费消息

        交换机类型:扇形交换机

        ②路由

         交换机会将消息转发给指定的队列

        交换机类型:定向交换机

        ③主题

         交换机会将消息转发给符合通配符的队列

         交换机类型:主题交换机


三、Rabbitmq死信队列

死信队列:死信队列中会存储死信消息

什么情况下消息会成为死信消息?

  1. 消息过期:消息设置过期时间后,如果没有被消费者消费,rabbitmq会将消息发送到死信队列。
  2. 消息被拒绝:消息被消费者消费时,无法处理该消息,此时可以将消息拒绝,将requeue设置为false。
  3. 队列达到最大长度:队列达到最大长度,无法发送到队列。

四、Rabbitmq延迟队列

rabbitmq中没有专门的延迟队列,可以通过ttl过期时间 + 死信队列实现延迟队列.

        生产者发送消息到队列中,当消息存活时间过期后,消息会被死信交换机放入到死信队列,最后在被消费者消费,达到实现延迟队列的效果。

五、Rabbitmq的高级特性-消息的可靠传输(保证消息不丢失)

消息传输的三个阶段,每个阶段都可能发生消息的丢失:

  1. 生产者发送消息到丢列
  2. rabbitmq存储消息
  3. 消费者消费消息:由于自动应答的情况,消费者在接收到消息后,会返回一个ack,此时rabbitmq会将消息直接从rabbitmq中删除,如果此时消费者出现宕机,消息就会丢失。

生产者保证消息不丢失:

        生产者回退机制

        回退机制

rabbitmq宕机:

        消息的持久化、交换机的持久化、队列的持久化

消费者导致消息丢失:

        开启手动应答:在消费者处理完消息后,手动告知 RabbitMQ 该消息已被成功处理并可以从队列中删除


自定义RabbitTemplate


import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Slf4j
@Configuration
public class RabbitMqConfiguration {

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {

        //创建rabbitTemplate对象
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //设置confirmCallBcak

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if (ack) {
                    log.info("消息正常发送给了交换机");
                } else {
                    log.info("消息没有正常发送给交换机,{}", cause); //cause失败原因
                }
            }
        });
        //设置returnCallBcak
        rabbitTemplate.setMandatory(true); //让mq服务端把消息返回
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                log.info("{}, {}, {}, {}, {}", message.toString(), replyCode, replyText, exchange, routingKey);
                log.info("发送给队列失败");
            }
        });

        return rabbitTemplate;


    }


}
publisher-confirm-type: correlated # 开启生产者确认机制
publisher-returns: true            # 开启生产者回退机制

自定义重试次数 

package com.atguigu.gmall.rabbit.service.impl;

import com.atguigu.gmall.rabbit.constant.RabbitConstant;
import com.atguigu.gmall.rabbit.service.RabbitBizService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.DigestUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息的有限重试
 */
@Slf4j
public class RabbitBizServiceImpl implements RabbitBizService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void retry(Channel channel, Long deliveryTag, String messageContent, int count) {
        //获取内容的md5加密
        String digestAsHex = DigestUtils.md5DigestAsHex(messageContent.getBytes(StandardCharsets.UTF_8));
        //从redis中获取剩余重试次数
        Long consumeCount = redisTemplate.opsForValue().increment(RabbitConstant.CONSUME_COUNT + digestAsHex);

        if (consumeCount <= 5) {
            //重试次数小于5,继续重试
            try {
                channel.basicNack(deliveryTag, true, true);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            //重试次数大于5次,
            try {
                //存入数据库
                log.info("存入数据库。。。。。");
                channel.basicAck(deliveryTag, true);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //redi中删除
            redisTemplate.delete(RabbitConstant.CONSUME_COUNT + digestAsHex);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值