RabbitMq消息队列

首页

控制台介绍

在这里插入图片描述
登录后页面如上图。overview页面时总览信息,connnections为连接信息,channels为通道信息,连接后建立通道。exchanges为交换机信息,queues为队列信息。admin为用户管理信息(操作用户的增删改)

消息发送

消息有生产者和发送者。最终的目的是生产者生产消息,由消费者消费。举个例子来讲,老师给同学布置作业。老师就是消息的生产者,同学就是消息的消费者。作业本身就是消息。
那么布置作业的形式久会有多种。

  1. 老师单独给某一个同学布置作业
  2. 老师给所有同学布置作业
  3. 老师给男同学布置作业
  4. 等等

在控制台实现消息发送

在控制台中,只有交换机和消息队列。消息的发送由交换机完成,消息的接收由队列完成。
在控制台中新建一个交换机
在这里插入图片描述
可以看到交换机的类型有4种,分别是fanout,headers,direct,topic。

  1. fanout:广播,将消息交给所有绑定到交换机的队列

新建一个fanout类型的交换机,点击进入交换机。在里面绑定已经建好的队列
在这里插入图片描述
将队列与交换机绑定后,通过交换机上的publish message发送消息,则可以在绑定的queue中获取消息数据。

  1. headers:header模式暂时没有场景
  2. direct:定向,把消息交给符合指定routing key 的队列

在交换机绑定队列的时候,指定routing key
在这里插入图片描述
消息发送的时候,设置routing key。如果设置了routing key为m,则只有路由key对应的队列才会收到消息。

  1. topic:通配符,相对于direct模式,可以把routing key设置为通配形式。
    在这里插入图片描述
    这个exchange绑定了两个queue,使用的routingKey分别是test.#和test.select。发送消息的时候,使用的routingKey是test.add。上面#代表多个,*代表一个。test.add满足了test.#的规范。所以消息路由到了queue_sms这个队列

springboot集成

增加maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

增加配置文件

spring:
  rabbitmq:
    host: 35.10.228.124
    port: 5672
    username: rabbitmq
    password: 123456

消息发送

在控制台中建好了exchange(交换机)和queue(队列)后,可以直接使用代码进行消息发送。也可以通过代码建交换机和队列

@SpringBootTest(classes = MyApplication.class)
@RunWith(SpringRunner.class)
public class Send {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void sendMsgByTopics() {
        rabbitTemplate.convertAndSend("exchange.sms", "routing.sms", "这是一条消息");
        System.out.println("send over");
    }
}

消息接收

@Component
public class RabbitMqReceiveHandler {
    @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "queue_sms"),
            exchange = @Exchange(value = "exchange.sms", type = ExchangeTypes.TOPIC)))
    public void receiveSms(String msg) {
        System.out.println("收到了消息:" + msg);
    }
}

使用@RabbitListener注解,接收消息。里面表名了交换机和队列。满足此绑定的消息即可被接收处理。消费者程序启动后,可以在控制台的connections中看到对应的连接内容
在这里插入图片描述
在通道(channels)中也可以看到所建立的通道
在这里插入图片描述
消息接收后,处理消息过程中,如果碰到异常情况,可能会有如下几种处理方式

  1. 告知服务器消息已经收到了,出错了,我不想再处理这个消息了。
  2. 告知服务器消息已经收到了,出错了,我想要再次处理这个消息。
    上面的处理方式中,使用了rabbitmq的自动确认机制。自动确认, 这也是默认的消息确认情况。 AcknowledgeMode.NONE
    RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。
    所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。
    一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。
    如果需要进行再次消费,则需要进行手动确认,下面进行手动确认的代码编写

手动确认

在消费者端建立一个MyReAckReciver类

package com.example.demo.mq;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Component

public class MyAckReceiver implements ChannelAwareMessageListener {

   @Override
   public void onMessage(Message message, Channel channel) throws Exception {
       long deliveryTag = message.getMessageProperties().getDeliveryTag();
       try {
           String msg = new String(message.getBody());
           System.out.println("消费的主题消息来自:" + message.getMessageProperties().getConsumerQueue());
           Integer integer = consumeMsg(msg);
           System.out.println(deliveryTag);
           if (integer > 5) {
               channel.basicAck(deliveryTag, true); //第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
           } else {
               channel.basicReject(deliveryTag, true);//第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
           }
       } catch (Exception e) {
           channel.basicReject(deliveryTag, false);
           e.printStackTrace();
       }
   }

   public Integer consumeMsg(String msg) {
       System.out.println(msg);
       try {
           TimeUnit.SECONDS.sleep(1);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       int i = new Random().nextInt(10);
       if (i == 5) {
           throw new RuntimeException("5 Exception");
       }
       return i;
   }
}

如何使用刚才建立的消息消费者。需要建立一个SimpleMessageListenerContainer类,这个类通过配置文件建立

package com.example.demo.mq;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MessageListenerConfig {

   @Autowired
   private CachingConnectionFactory connectionFactory;
   @Autowired
   private MyAckReceiver myAckReceiver;//消息接收处理类

   @Bean
   public SimpleMessageListenerContainer simpleMessageListenerContainer() {
       SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
       container.setConcurrentConsumers(1);
       container.setMaxConcurrentConsumers(1);
       container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
       //设置一个队列
       container.setQueueNames("TestDirectQueue","queue_sms");
       //如果同时设置多个如下: 前提是队列都是必须已经创建存在的
       //  container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3");


       //另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues
       //container.setQueues(new Queue("TestDirectQueue",true));
       //container.addQueues(new Queue("TestDirectQueue2",true));
       //container.addQueues(new Queue("TestDirectQueue3",true));
       container.setMessageListener(myAckReceiver);

       return container;
   }


}

此时,生产者发送消息后,消费者就会使用MyAckReceiver进行消息的消费。我们在SimpleMessageListenerContainer中设置了消息的回复机制为AcknowledgeMode.MANUAL (手动确认)。在MyAckReceiver中消息消费,消息消费模拟了3中情况,一种是满足条件的正常返回(>5),一种是不满足情况的正常返回(<5),还有一种是异常抛出(=5)。

当满足条件的正常返回时,我们使用了肯定确认channel.basicAck(deliveryTag, true); 在这个ack方法中,第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息。

当不满足条件的正常返回时,我们使用了否定确认。在regict方法中,第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝。这种情况要慎用,如果考虑不周,则会出现消息一直入队消费的死循环,导致消息堆积。设置false,就是告诉服务器,我已经知道这条消息数据了,因为一些原因拒绝它,而且服务器也把这个消息丢掉就行。 下次不想再消费这条消息了
当抛出异常时,就使用了reject的false选项。表示我知道这个消息了,已经丢掉了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值