RabbitMQ

  1. RabbitMQ的工作模式

(1)工作队列模式

(2)发布订阅模式

(多了一个exchange交换机)

(3)路由模式

(需要指定routing key路由)

(4)通配符模式

通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert

2.SpringBoot和rabbitMQ的整合

  1. producer模块中,yml配置文件

# 配置RabbitMQ的基本信息  ip 端口 username  password..
spring:
  rabbitmq:
    host: xxxxx # ip
    port: 5672
    username: bq123
    password: bq123
    virtual-host: /baiqi
  1. 声明一个配置类,把交换机队列进行绑定

 @Configuration
public class RabbitMqConfig {

    //定义交换机的名字
    public static final String  EXCHANGE_NAME = "boot_topic_exchange";
    //定义队列的名字
    public static final String QUEUE_NAME = "boot_queue";

    //1、声明交换机
    @Bean("bootExchange")
    public Exchange bootExchange(){

        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
    }



    //2、声明队列
    @Bean("bootQueue")
    public Queue bootQueue(){

        return QueueBuilder.durable(QUEUE_NAME).build();
    }


    //3、队列与交换机进行绑定
    @Bean
    public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
        //这个 boot.#是 声明一个路由的key   (topic类型的交换机)
        return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
    }

}
  1. 注入rabbitTemplate @Autowired,完成数据的绑定

@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {

     //注入 RabbitTemplate
    @Autowired
     private RabbitTemplate  rabbitTemplate;

     @Test
     public void send(){
        //交换机的名字    
         rabbitTemplate.convertAndSend("boot_topic_exchange","boot.haha","boot mq...");
     }
}
  1. 消费者监听器 @RabbitListener,完成对数据的监听

@Component
public class RabbitMQListener {

      //定义方法进行信息的监听   RabbitListener中的参数用于表示监听的是哪一个队列
      @RabbitListener(queues = "boot_queue")
      public void ListenerQueue(Message message){
          System.out.println("message:"+message.getBody());
      }
}

3.RabbitMQ的高级特性

(1)Producer保证消息的可靠性传递 (confirm,return) 与Consumer ACK

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

  • confirm 确认模式

  • return 退回模式

rabbitmq 整个消息投递的路径为:

producer--->rabbitmq broker--->exchange--->queue--->consumer

消息从 producer 到 exchange 则会返回一个 confirmCallback

消息从 exchange-->queue 投递失败则会返回一个 returnCallback

我们将利用这两个 callback 控制消息的可靠性投递

   <!--消息可靠性投递(生产端)-->   xml
    <rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>

    <rabbit:direct-exchange name="test_exchange_confirm">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

测试1:确认模式

    @Test
    public void testConfirm() {

         //定义回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 相关配置信息
             * @param ack   exchange交换机 是否成功收到了消息。true 成功,false代表失败
             * @param cause 失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm方法被执行了....");

                 //ack 为  true表示 消息已经到达交换机
                if (ack) {
                    //接收成功
                    System.out.println("接收成功消息" + cause);
                } else {
                    //接收失败
                    System.out.println("接收失败消息" + cause);
                    //做一些处理,让消息再次发送。
                }
            }
        });

           //进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message Confirm...");

        //进行睡眠操作
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

测试2:退回模式

 @Test
    public void testReturn() {

        //设置交换机处理失败消息的模式   为true的时候,消息达到不了 队列时,会将消息重新返回给生产者
        rabbitTemplate.setMandatory(true);

        //定义回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message   消息对象
             * @param replyCode 错误码
             * @param replyText 错误信息
             * @param exchange  交换机
             * @param routingKey 路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return 执行了....");

                System.out.println("message:"+message);
                System.out.println("replyCode:"+replyCode);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);

                //处理
            }
        });
        //进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message return...");

        //进行睡眠操作
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Consumer --- ACK (implements ChannelAwareMessageListener)

  <!--定义监听器容器
      acknowledge="manual":手动签收
      prefetch="1":每次抓取多少条消息
    -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >
    <rabbit:listener ref="ackListener" queue-names="test_queue_confirm"></rabbit:listener>
</rabbit:listener-container>

注意 channel.basicAck(deliveryTag, true);

channel.basicNack(deliveryTag, true, true);

@Component
public class AckListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //1、获取消息的id
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {


        //2、获取消息
        System.out.println("message:"+new String(message.getBody()));

        //3、进行业务处理
        System.out.println("=====进行业务处理====");

        //模拟出现异常
        int  i = 5/0;

        //4、进行消息签收
        channel.basicAck(deliveryTag, true);

        } catch (Exception e) {

            //拒绝签收
             /*
            第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
             */
            channel.basicNack(deliveryTag, true, true);

        }
    }
}

(2)消费端限流 prefetch="2"
  <!--定义监听器容器
      acknowledge="manual":手动签收
      prefetch="1":每次抓取多少条消息
    -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >
    <rabbit:listener ref="ackListener" queue-names="test_queue_confirm"></rabbit:listener>
</rabbit:listener-container>

(3)TTL (time to live 存活时间)

key="x-message-ttl" 这个是固定的

 <rabbit:queue name="test_queue_ttl" id="test_queue_ttl">
        <!--设置queue的参数-->
        <rabbit:queue-arguments>
            <!--x-message-ttl指队列的过期时间-->
            <entry key="x-message-ttl" value="100000" value-type="java.lang.Integer"></entry>
        </rabbit:queue-arguments>

    </rabbit:queue>

   <rabbit:topic-exchange name="test_exchange_ttl" >
        <rabbit:bindings>
            <rabbit:binding pattern="ttl.#" queue="test_queue_ttl"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

(4)死信队列 (Dead Letter Exchange 死信交换机)

死信队列:

1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx)

2. 声明死信队列(queue_dlx)和死信交换机(exchange_dlx)

3. 正常队列绑定死信交换机

设置两个参数:

* x-dead-letter-exchange:死信交换机名称

* x-dead-letter-routing-key:发送给死信交换机的routingkey

    <!--
        1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx)
    -->

   <rabbit:queue name="test_queue_dlx" id="test_queue_dlx">
        <! -- 正常队列绑定死信交换机-->
        <rabbit:queue-arguments>
            <! --3.1 x-dead-letter-exchange:死信交换机名称-->
            <entry key="x-dead-letter-exchange" value="exchange_dlx" />

            <! --3.2 x-dead-letter-routing-key:发送给死信交换机的routingkey-->
            <entry key="x-dead-letter-routing-key" value="dlx.hehe" />

            <! -- 4.1 设置队列的过期时间 ttl -->
            <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
            <! -- 4.2 设置队列的长度限制 max-length -->
            <entry key="x-max-length" value="10" value-type="java.lang.Integer" />
        </rabbit:queue-arguments>
    </rabbit:queue>
    <rabbit:topic-exchange name="test_exchange_dlx">
        <rabbit:bindings>
            <rabbit:binding pattern="test.dlx.#" queue="test_queue_dlx"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--2. 定义死信交换机(order_exchange_dlx)和队列(order_queue_dlx)-->
    <rabbit:queue id="order_queue_dlx" name="order_queue_dlx"></rabbit:queue>
    <rabbit:topic-exchange name="order_exchange_dlx">
        <rabbit:bindings>
            <rabbit:binding pattern="dlx.order.#" queue="order_queue_dlx"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

消费者的监听器 监听死信交换机的下一个队列:order_queue_dlx

 <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2" >

       <!--延迟队列效果实现:  一定要监听的是 死信队列!!!-->
       <rabbit:listener ref="orderListener" queue-names="order_queue_dlx"></rabbit:listener>

    </rabbit:listener-container>

(5)延时队列 (很可惜,在RabbitMQ中并未提供延迟队列功能)

但是可以使用:TTL+死信队列 组合实现延迟队列的效果

(6)消息幂等性保障

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值