SpringBoot整合RabbitMQ实现消息确认和失败消息重发

  1. 准备工作
    安装在安装完rabbitMq后,输入http://ip:15672/ ,是可以看到一个简单后台管理界面的。

  2. RabbitMq简介
    SpringBoot整合RabbitMQ实现消息确认和失败消息重发_第1张图片
    常用的交换机有以下三种,因为消费者是从队列获取信息的,队列是绑定交换机的(一般),所以对应的消息推送/接收模式也会有以下几种:
    1)Direct Exchange
    直连型交换机,根据消息携带的路由键将消息投递给对应队列。
    大致流程,有一个队列绑定到一个直连交换机上,同时赋予一个路由键 routing key 。然后当一个消息携带着路由值为X,这个消息通过生产者发送给交换机时,交换机就会根据这个路由值X去寻找绑定值也是X的队列。

2)Fanout Exchange
扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。

3)Topic Exchange
主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。
简单地介绍下规则
(*号) 用来表示一个单词 (必须出现的)
(#号) 用来表示任意数量(零个或多个)单词
通配的绑定键是跟队列进行绑定的,举个小例子
队列Q1 绑定键为 .TT. 队列Q2绑定键为 TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到;
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到

3.下面写了一个以直型交换机为的demo里面有消息确认,失败消息重发机制

创建两个springboot工程 一个为提供者 一个为消费者

  1. 添加依赖(共同)
   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
  1. yml文件 (共同)
spring:
  rabbitmq:
    username: guest
    port: 5672
    password: guest
    host: localhost
    virtual-host: /
    #开启消息确认模式
    publisher-returns: true
    publisher-confirms: true
    template:
      # 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual #手动ACK
        default-requeue-rejected: false #个字段一定要设置成 false 不然无法消费的数据不会进入死信队列的
        concurrency: 1 #同一个队列启动几个消费者
        max-concurrency: 1 #启动消费者最大数量
        prefetch: 1 #限制每次发送一条数据
        retry:
          enabled: true #是否支持重试
  1. 消费者 创建一个监听消费消息的类ReceiveHandler
@Component
public class ReceiveHandler {
     

    @RabbitListener(queues = {
     RabbitmqConfig.QUEUE_INFORM_EMAIL})
    public void send_email(Message message, Channel channel) throws IOException {
     
        // 采用手动应答模式, 手动确认应答更为安全稳定
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        System.out.println("receive message is:"+ new String(message.getBody(),"utf-8"));
    }

}
  1. 消息提供者
    4.1 创建 rabbitmq配置类,里面设置路由键,队列,交换机等
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitmqConfig {
     

    //声明队列
    public static final String QUEUE_INFORM_EMAIL = "email_queue";
    public static final String QUEUE_INFORM_SMS = "sms_queue";

    //直型交换机名字
    public static final String EXCHANGE_DECLARE_INFORM="xuecheng_exchange";

    //路由键
    public static final String ROUTINGKEY_EMAIL="inform.email";
    public static final String ROUTINGKEY_SMS="inform.sms";

    //声明  直型交换机
    @Bean(EXCHANGE_DECLARE_INFORM)
    public Exchange EXCHANGE_TOPICS_INFORM(){
     
        return new DirectExchange(EXCHANGE_DECLARE_INFORM);
    }

    //声明 队列
    //声明 QUEUE_INFORM_EMAIL队列
    @Bean(QUEUE_INFORM_EMAIL)
    public Queue QUEUE_INFORM_EMAIL(){
     
        return new Queue(QUEUE_INFORM_EMAIL,true); //true 是否持久
    }
    //声明 QUEUE_INFORM_SMS队列
    @Bean(QUEUE_INFORM_SMS)
    public Queue QUEUE_INFORM_SMS(){
     
        return new Queue(QUEUE_INFORM_SMS,true); //true 是否持久
    }

    //绑定队列
    //ROUTINGKEY_SMS队列绑定交换机,指定routingKey
    @Bean
    public Binding BINDING_ROUTINGKEY_SMS(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
                                          @Qualifier(EXCHANGE_DECLARE_INFORM) Exchange exchange){
     
        return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_SMS).noargs();
    }
   // QUEUE_INFORM_EMAIL队列绑定交换机,指定routingKey
    @Bean
    public Binding BINDING_ROUTINGKEY_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
                                          @Qualifier(EXCHANGE_DECLARE_INFORM) Exchange exchange){
     
        return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs();
    }

}

4.2 创建发送消息工具类,里面有消息确认,失败消息重发

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.amqp.core.MessageProperties;

@Component
public class RabbitSend implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback{
     

    private RabbitTemplate rabbitTemplate;

    @Autowired
    public RabbitSend(RabbitTemplate rabbitTemplate) {
     
        super();
        this.rabbitTemplate = rabbitTemplate;
        this.rabbitTemplate.setMandatory(true);
        this.rabbitTemplate.setReturnCallback(this);
        this.rabbitTemplate.setConfirmCallback(this);
    }

    /**
     * 发布消息
     * @param
     */
    public void routeSend(String message,String routingKey) {
     
        Message messages = this.setMessage(message);
        //在fanoutExchange中在绑定Q到X上时,会自动把Q的名字当作bindingKey。
        rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_DECLARE_INFORM,routingKey,messages);
    }


    /**
     * 设置消息参数
     * @param json
     * @return
     */
    private Message setMessage(String json){
     
        MessageProperties messageProperties = new MessageProperties();
        Message message = new Message(json.getBytes(), messageProperties);
        //消息持久化
        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        return message;
    }

    // 消息确认
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
     
        if (ack) {
     
            System.out.println("消息发送确认成功==========");
        } else {
     
            System.out.println("消息发送失败=========="+ cause);
        }
    }

    //消息发送失败回传
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
     
        System.out.println("return--message:" + new String(message.getBody()) + ",replyCode:" + replyCode + ",replyText:"
                + replyText + ",exchange:" + exchange + ",routingKey:" + routingKey);
        try {
     
            Thread.sleep(10000L);
            // TODO 重新发送消息至队列,此处应写一套重发机制,重发多少次结束,否则如果消息如果一直发送失败,则会一直发下去!
            this.rabbitTemplate.convertAndSend(exchange, routingKey, message);
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
}
  1. 测试
@RestController
@RequestMapping("/rabitmq")
public class testController {
     

    @Autowired
    private RabbitSend rabbitSend;

    @GetMapping("/t")
    public void t(){
     
        Map<String,Object> map = new HashMap<>();
        map.put("msg","这是第一个消息");
        map.put("data", Arrays.asList("helloworld",123,true));
        rabbitSend.routeSend(map.toString(),RabbitmqConfig.ROUTINGKEY_EMAIL);
    }
}

访问 http://localhost:11111/rabitmq/t
消费者打印日志为:
SpringBoot整合RabbitMQ实现消息确认和失败消息重发_第2张图片
生产者打印日志为
在这里插入图片描述
则成功

(源码:https://gitee.com/zhu_can_admin/rabbitmq.git)

你可能感兴趣的:(rabbitmq)