RabbitMQ quick early adopters

Original: WeChat public account [Ah Q said code], welcome to share, please keep the source for reprinting.

We have completed the installation above , we need to let it play a role after installation, integrate it RabbitMQin the project today , try it out!SpringBoot

Before the project actually starts, let's briefly introduce RabbitMQthe workflow below:

  • The producer sends a message to the exchange;
  • The switch binds the queue through the rules, and stores the message in the queue through the routing key;
  • The consumer obtains the messages in the queue for consumption;

Environment: SpringBoot 2.6.3, JDK 1.8

Project build

SpringBootCreate the project firstrabbit-mq

  1. Introduce dependencies
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. yml file configuration
spring:
  rabbitmq:
    host: 127.0.0.1     //rabbitMQ服务地址
    port: 15672   //这个地方暂时先用我们之前配置的15672
    username: cheetah   //自己的账户名
    password: 123456    //自己的密码
  1. direct switch

This project takes direct-connected switches as an example, and other types of switches will be introduced in detail later.

@Configuration
public class DirectRabbitConfig {
    
    

    /**
     * 定义交换机
     **/
    @Bean
    public DirectExchange directExchange(){
    
    
        /**
         * 交换机名称
         * 持久性标志:是否持久化,默认是 true 即声明一个持久的 exchange,该exchange将在服务器重启后继续运行
         * 自动删除标志:是否自动删除,默认为 false, 如果服务器想在 exchange不再使用时删除它,则设置为 true
         **/
        return new DirectExchange("directExchange", true, false);
    }

    /**
     * 定义队列
     **/
    @Bean
    public Queue directQueue(){
    
    
        /**
         * name:队列名称
         * durable:是否持久化,默认是 true,持久化队列,会被存储在磁盘上,当消息代理重启时仍然存在
         * exclusive:是否排他,默认为 false,true则表示声明了一个排他队列(该队列将仅由声明者连接使用),如果连接关闭,则队列被删除。此参考优先级高于durable
         * autoDelete:是否自动删除, 默认是 false,true则表示当队列不再使用时,服务器删除该队列
         **/
        return new Queue("directQueue",true);
    }

    /**
     * 队列和交换机绑定
     * 设置路由键:directRouting
     **/
    @Bean
    Binding bindingDirect(){
    
    
        return BindingBuilder.bind(directQueue()).to(directExchange()).with("directRouting");
    }


}
  1. send message
@RestController
public class SendMessageController {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/sendMessage")
    public String sendMessage(){
    
    
        //将消息携带路由键值
        rabbitTemplate.convertAndSend("directExchange", "directRouting", "发送消息!");
        return "ok";
    }

}

Let's start the program first, under browser access

http://127.0.0.1:9001/sendMessage

The error is as follows:

We have assigned permissions to this user before, if not assigned before, configure it directly in the client:

The reason why it cannot be accessed is because the port number we are using is incorrect

So we need to change the port to 5672(if it is an Alibaba Cloud server instance, the port needs to be opened )

let's visit again

http://127.0.0.1:9001/sendMessage

The request returns "OK", the console output

A screenshot of the relevant client page is as follows:




  1. message consumption
@Component
@RabbitListener(queues = "directQueue")//监听队列名称
public class MQReciever {
    
    

    @RabbitHandler
    public void process(String message){
    
    
        System.out.println("接收到的消息是:"+ message);
    }
}

Start the project and find that the message has been consumed.

In order to prevent message loss, RabbitMQa message confirmation mechanism is added: producer message confirmation mechanism and consumer message confirmation mechanism.

confirmation mechanism

1. Producer message confirmation mechanism

  1. ymlAdd configuration information in
spring:
  rabbitmq:
    #确认消息已发送到交换机(Exchange)
    publisher-confirm-type: correlated
    #确认消息已发送到队列(Queue)
    publisher-returns: true

The new version of spring.rabbitmq.publisher-confirm has been deprecated, now use spring.rabbitmq.publisher-confirm-type=correlated to achieve the same effect

  1. add callback
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
    
    
	RabbitTemplate rabbitTemplate = new RabbitTemplate();
	rabbitTemplate.setConnectionFactory(connectionFactory);
	//设置开启 Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
	rabbitTemplate.setMandatory(true);

	rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
    
    
		@Override
		public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    
    
			System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);
			System.out.println("ConfirmCallback:     "+"确认情况:"+ack);
			System.out.println("ConfirmCallback:     "+"原因:"+cause);
		}
	});

	rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){
    
    
		@Override
		public void returnedMessage(ReturnedMessage returned) {
    
    
			System.out.println("ReturnCallback:     "+"消息:"+returned.getMessage());
			System.out.println("ReturnCallback:     "+"回应码:"+returned.getReplyCode());
			System.out.println("ReturnCallback:     "+"回应信息:"+returned.getReplyText());
			System.out.println("ReturnCallback:     "+"交换机:"+returned.getExchange());
			System.out.println("ReturnCallback:     "+"路由键:"+returned.getRoutingKey());
		}
	});
	return rabbitTemplate;
}
  • confirmThe mechanism only guarantees that the message arrives exchange, and does not guarantee that the message can be routed to the correctqueue
  • The mechanism will only be triggered if the current exchangeroute does not exist or the specified route is not available.keyreturn

You can demonstrate the execution results of the following situations by yourself:

  • There are no switches and queues
  • There are switches, there are no queues
  • Message pushed successfully

2. Confirmation mechanism of consumer messages

By default a message is removed from the queue if it is correctly received by the consumer. If a queue is not subscribed by any consumer, the messages in this queue will be cached, and will be sent immediately when a consumer subscribes, and then removed from the queue.

The confirmation mechanism of consumer messages can be divided into the following three types:

  1. auto confirm

AcknowledgeMode.NONEThe default is automatic confirmation, regardless of whether the consumer successfully processed the message, the message will be removed from the queue.

  1. Confirm according to the situation

AcknowledgeMode.AUTODetermine whether to confirm or reject according to the execution of the method (whether to re-queue)

  • If the message is successfully consumed (success means that no exception is thrown during the consumption process), then automatic confirmation
  • When an exception is thrown AmqpRejectAndDontRequeueException, the message will be rejected and the message will not be re-queued
  • When ImmediateAcknowledgeAmqpExceptionan exception is thrown, the consumer will be confirmed
  • For other exceptions, the message will be rejected, and the message will be returned to the queue. If only one consumer listens to the queue at this time, there is a risk of an infinite loop, and multiple consumers will also cause a great waste of resources. This Must be avoided during development. Can be set by setDefaultRequeueRejected(default is )true

It may cause message loss. Generally, try-catchafter catching the exception, we need to print the log to track the data, so as to find the corresponding data and then do subsequent processing.

  1. manual confirmation

AcknowledgeMode.MANUALFor manual confirmation, it is also the most commonly used in our work, and its usage is as follows:

/*
 * 肯定确认
 * deliveryTag:消息队列数据的唯一id
 * multiple:是否批量 
 * true :一次性确认所有小于等于deliveryTag的消息
 * false:对当前消息进行确认;
 */
channel.basicAck(long deliveryTag, boolean multiple); 
/*
 * 否定确认
 * multiple:是否批量 
 *   true:一次性拒绝所有小于deliveryTag的消息
 *   false:对当前消息进行确认;
 * requeue:被拒绝的是否重新入列,
 *   true:就是将数据重新丢回队列里,那么下次还会消费这消息;
 *   false:就是拒绝处理该消息,服务器把该消息丢掉即可。 
 */
channel.basicNack(long deliveryTag, boolean multiple, boolean requeue);
/*
 * 用于否定确认,但与basicNack相比有一个限制,一次只能拒绝单条消息
 */
channel.basicReject(long deliveryTag, boolean requeue);  

manual confirmation

Enable manual confirmation mode in yml configuration

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual

or turn it on in code

@Configuration
public class MessageListenerConfig {
    
    

    @Autowired
    private CachingConnectionFactory connectionFactory;

    @Autowired
    private MQReciever mqReciever;//消息接收处理类

    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(){
    
    
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //并发使用者的数量
        container.setConcurrentConsumers(1);
        //消费者人数上限
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
        //设置一个队列,此处支持设置多个
        container.setQueueNames("directQueue");
        container.setMessageListener(mqReciever);
        return container;
    }
}

message consumer

@Component
@RabbitListener(queues = "directQueue")//监听队列名称
public class MQReciever implements ChannelAwareMessageListener {
    
    

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
    
    
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
    
    
            String msg = message.toString();
            String[] msgArray = msg.split("'");//可以点进Message里面看源码,单引号直接的数据就是我们的map消息数据
            System.out.println("消费的消息内容:"+msgArray[1]);
            System.out.println("消费的主题消息来自:"+message.getMessageProperties().getConsumerQueue());
            
            //业务处理
            ......
            
            channel.basicAck(deliveryTag, true);
            
        } catch (Exception e) {
    
    
            //拒绝重新入队列
            channel.basicReject(deliveryTag, false);   
            e.printStackTrace();
        }
    }
}

No ack: high efficiency, there is a risk of losing a large number of messages; with ack: low efficiency, no message loss.

Ah Q will continue to update articles on java combat. If you are interested, you can pay attention to it, and you can also come to the technical group to discuss problems!

Guess you like

Origin blog.csdn.net/Qingai521/article/details/124664842