广播模式的简单描述

通过对RabbitMQ的学习,可以发现,消息都是通过交换器发送至队列的,一条消息只能被一个消费者处理,实际开发中还会有一种情况,就是一条消息需要被多个消费者处理,就是广播的形式;广播的模式需要使用到FanoutExchange(散列交换器),FanoutExchange 会将消息发送至每一个与之绑定的队列中。

具体使用:

1. 引入Rabbit MQ依赖

<!-- Rabbit Mq依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 修改配置文件

spring: 
  rabbitmq:
  	# [主机IP,不加默认为本机地址]
    host: 127.0.0.1
    # [端口号,不加默认为5672]
    port: 5672
    #消费端配置
    listener:
      simple:
        #自动签收auto  手动 manual
        acknowledge-mode: manual

3. 编写配置类

配置广播模式的消息队列:
创建一个名为RabbitmqConfigFanout的配置类,在该配置类上使用@Configuration 注解标明这是一个配置类;
通过@Bean注解使用注解的name属性来指定消息队列名,创建两个消息队列,其返回类型为Queue;
同样地,通过@Bean注解创建一个广播模式的交换机,其返回类型为FanoutExchange。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author by陌影
 * @tool Intellij IDEA
 * @classname IncrQueueConfig 消息初始化队列
 * @date 2022/5/28 22:14
 */
@Configuration
public class RabbitmqConfigFanout {
	/** 日志打印*/
    private final Logger log = LoggerFactory.getLogger(RabbitmqConfigFanout.class);

    /***************************  初始化消息队列 ***************************/
    /**
     * 初始化队列A -> xx功能
     * 第二个参数TRUE开启消息签收
     */
    @Bean(name = MqConstants.QUEUE_A)
    public Queue queueA() {
        Queue queue = new Queue(MqConstants.QUEUE_A,true);
        log.info("队列:[{}]初始化成功................",queue.getName());
        return queue;
    }
    
    /**
     * 初始化队列B -> xx功能
     * 第二个参数TRUE开启消息签收
     */
    @Bean(name = MqConstants.QUEUE_B)
    public Queue queueB() {
        Queue queue = new Queue(MqConstants.QUEUE_B,true);
        log.info("队列:[{}]初始化成功................",queue.getName());
        return queue;
    }

    /***************************  初始化交换机 ***************************/
    @Bean
    public FanoutExchange fanoutExchange() {
        FanoutExchange fanoutExchange = new FanoutExchange(MqConstants.EXCHANGE);
        log.info("交换机:[{}]初始化成功................",fanoutExchange.getName());
        return fanoutExchange;
    }

    /***************************  绑定交换机 ***************************/
    /*通过@Qualifier注解指定要绑定的队列A到交换机*/
    @Bean
    public Binding bindingQueueA(@Qualifier(MqConstants.QUEUE_A) Queue queue, FanoutExchange fanoutExchange) {
        Binding binding = BindingBuilder.bind(queue).to(fanoutExchange);
        log.info("消息队列:[{}] 成功绑定到交换机:[{}] ................",queue.getName(),fanoutExchange.getName());
        return binding;
    }
    /*绑定队列B到交换机*/
    @Bean
    public Binding bindingQueueB(@Qualifier(MqConstants.QUEUE_B) Queue queue, FanoutExchange fanoutExchange) {
        Binding binding = BindingBuilder.bind(queue).to(fanoutExchange);
        log.info("消息队列:[{}] 成功绑定到交换机:[{}] ................",queue.getName(),fanoutExchange.getName());
        return binding;
    }
}

4. 编写生产者(发送者)类

创建一个发送者类,在类上使用组件注解@Component表明这是一个组件,如果不添加此注解,在使用@Autowired自动注入(自动装配)时,启动项目会报错误。
在类的内部通过@Autowired自动注入RabbitMQ的消息队列的模板(RabbitTemplate)。
编写一个发送方法,使用模板RabbitTemplate的convertAndSend()方法发送消息。

具体代码:

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class FanoutSender {
	/** 日志打印*/
    private final Logger log = LoggerFactory.getLogger(FanoutSender.class);
    
	@Autowired
    private RabbitTemplate rabbitTemplate;
	
	public void send() {
		 try {
            String str = "Fanout";
			System.out.println("广播模式发送者Sender:" + str);
        } finally {
            rabbitTemplate.convertAndSend(MqConstants.EXCHANGE, "", "要发送的消息");
        }
	}
}

5. 编写消费者(接收者)类

创建一个接收者类,类上使用注解@Component和RabbitMQ的监听注解@RabbitListener,其中@RabbitListener使用其属性queues绑定需要监听的队列。
创建一个接收方法process(),方法上使用处理注解@RabbitHandler。

接收者A的具体代码如下:

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = {MqConstants.QUEUE_A})
public class FanoutReceiverA {

	/**
     * @param msg		接收到的消息
     * @param message   消息主题
     * @param channel   队列签收
     */
	@RabbitHandler
	public void process(String msg, Message message, Channel channel) {
		long startTime = System.currentTimeMillis();
        log.info("消息队列:[{}],接收到消息:[{}]",MqConstants.QUEUE_A, msg);

        try {
            //处理消息
            //此处是对消息的处理

            //签收消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
            log.info("消息队列[{}],接收到消息:[{}],消费完成 耗时:[{}ms]",MqConstants.QUEUE_A, msg, System.currentTimeMillis() - startTime);
        }catch (Exception e) {
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                log.info("消息队列[{}],接收到消息:[{}],消费失败 耗时:[{}ms]",MqConstants.QUEUE_A, msg, System.currentTimeMillis() - startTime);
            }catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }
	}
}

接收者B的具体代码如下:

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = {MqConstants.QUEUE_B})
public class FanoutReceiverB {

	/**
     * @param msg		接收到的消息
     * @param message   消息主题
     * @param channel   队列签收
     */
	@RabbitHandler
	public void process(String msg, Message message, Channel channel) {
		long startTime = System.currentTimeMillis();
        log.info("消息队列:[{}],接收到消息:[{}]",MqConstants.QUEUE_B, msg);

        try {
            //处理消息
            //此处是对消息的处理

            //签收消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
            log.info("消息队列[{}],接收到消息:[{}],消费完成 耗时:[{}ms]",MqConstants.QUEUE_B, msg, System.currentTimeMillis() - startTime);
        }catch (Exception e) {
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                log.info("消息队列[{}],接收到消息:[{}],消费失败 耗时:[{}ms]",MqConstants.QUEUE_B, msg, System.currentTimeMillis() - startTime);
            }catch (IOException ioException) {
                throw new RuntimeException(ioException);
            }
        }
	}
}

6. 进行测试

编写一个测试类,将生产者的类使用@Autowired 注解进行注入,调用生产方法,最终达到效果