rabbitmq

RabbitMQ高级特性

消息可靠性投递

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

  • confirm 确认模式
  • return 追回模式

rabbitmq整个消息投递路径为:producer—>exchange—>queue—>consumer

  1. 消息从producer到exchange则会返回一个confirmCallback
  2. 消息从exchange—>queue投递失败则会返回一个returnCallback.

利用这两个callback控制消息的可靠性传递

//配置信息
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-confirms: true
    publisher-returns: true
package com.qf;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.Callable;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProviderApplicationTests {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void contextConfirm() {
        /*
         * 确认模式:
         *   步骤:
         *       1.确认模式开启:publisher-confirms: true
         *       2.在rabbitTemplate定义confirmCallback回调函数
         * */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
            * @Author: Mr.Zhou
            * @Date: 2021/3/2 23:31
            * @Description: confirm
            * @param correlationData:相关配置信息
             * @param b: exchange交换机是否成功收到了消息,true代表成功,false代表失败
             * @param s: 当b为false时,s会显示失败原因
            * @Return: void
            */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b){
                    //接收成功
                    System.out.println("消息接收成功");
                }else{
                    //接收失败,处理消息再次发送
                    System.out.println("消息接受失败,失败原因:"+s);
                }
                System.out.println("confirmCallback执行了......");
            }
        });
        //3.发送消息
        rabbitTemplate.convertAndSend("test_exchange","confirm","message confirm ....");
    }
    @Test
    public void contextReturn() {
        /*
        * 回退模式:当消息发送给Exchange后,Exchange路由到Queue失败才会执行ReturnCallback
        *   步骤:
        *       1.开启回退模式  publisher-returns: true
        *       2.设置ReturnCallBack
        *       3.设置Exchange处理消息的模式
        *           1.如果消息路由到queue,则丢弃消息(默认)
        *           2.如果消息没有路由到queue,返回消息发送方ReturnCallBack
        * */
        //设置交换机处理失败消息模式
        rabbitTemplate.setMandatory(true);
        //设置returnCallBack
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
            * @Author: Mr.Zhou
            * @Date: 2021/3/4 11:09
            * @Description: returnedMessage
            * @param message: 消息对象
             * @param i: 错误码
             * @param s: 错误信息
             * @param s1: 交换机
             * @param s2: 路由键
            * @Return: void
            */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("消息对象:"+message);
                System.out.println("错误信息:"+s);
                System.out.println("交换机:"+s1);
                System.out.println("路由键:"+s2);
                System.out.println("return执行了....");
            }
        });
        rabbitTemplate.convertAndSend("test_exchange","confirm1","message confirm ....");
    }
}
Consumer ACK

ack指Acknowledge,确认,表示消费端收到消息后的确认方式.

有三种确认方式:

  • 自动确认:acknowledge=“none”
  • 手动确认:acknowledge=“manual”
  • 根据异常情况确认:acknowledge=“auto”(这种方式使用麻烦)

自动确认是指,当消息一旦被consumer接收到,则自动确认收到,并将相应message从rabbitmq消息缓存中移除,但是实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失,如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息.

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual #开启手动ack
package com.qf.controller;/*
 * Description:
 * User:Hello,Mr.Zhou
 * Date:2021-03-04
 * Time:11:36
 *
 * */

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

import java.io.IOException;

/*
* Consumer ACK机制
*   1.设置手动签收.acknowledge-mode: manual
*   2.让监听器类实现ChannelAwareMessageListener接口
*   3.如果消息成功处理,则调用channel的basicAck()签收
*   4.如果消息处理失败,则调用channel的basicNack()拒绝签收,broker重新发送给consumer
*
* */
@Component
public class AckListenter implements ChannelAwareMessageListener {
    @RabbitListener(queues = "test_queue")
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //1.接受转换消息
            System.out.println(new String(message.getBody()));
            //2.处理业务逻辑
            System.out.println("处理业务逻辑");
            //3.手动签收
//            int i=1/0;
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
//            e.printStackTrace();
            //4.拒绝签收
            /*
            * 第三个参数:requeue:重回队列,如果设置为true,消息重新回到队列,broker会重新发送消息给消费端
            * */
            channel.basicNack(deliveryTag,true,true);
        }
    }
}
  • 在配置文件中设置acknowledge属性,设置ack方式none:自动确认,manual:手动确认
  • 如果在消费端出现异常,则调用channel.basicAck(deliveryTag.false);方法确认签收消息
  • 如果出现异常,则在catch中调用basicNack或basicReject,拒绝接受消息,让mq重新发送消息
消息端限流
  • 配置prefetch属性设置消费端一次拉取多少条消息
  • 消费端的确认模式一定为手动确认.acknowledge=“manual”
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual #开启手动ack
        prefetch: 1 #一次接受一条消息
package com.qf.controller;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
/*
*
* @Author: Mr.Zhou
* @Date: 2021/3/4 20:04
* @Description: consumer 限流机制
* @param null:
* @Return:
*/
/*
* 1.确保ack机制为手动确认.
* 2.listener-container配置属性
*   perfectch = 1,表示消费端每次从mq拉出一条消息来消费,直到确认消费完毕后,才会继续拉下一条消息.
*
* */
@Component
public class QosListenter implements ChannelAwareMessageListener {
    @RabbitListener(queues = "test_queue")
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //1.获取消息
        System.out.println(new String(message.getBody()));
        //2.处理业务
        //3.签收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
    }
}
TTL
  • TTL全称Time To Live (存活时间/过期时间)
  • 当消息到达存活时间后,还没有被消费,会被自动清除
  • RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间

生产者[订单系统] --------> 中间件{30分钟} ------------>消费者[支付系统]

30分钟消息未被消费自动取消

死信队列

死信队列,英文缩写:DLX.Dead Letter Exchange(死信交换机)当消息成为死信后,可以被重新发送到另一个交换机,这个交换机就是DLX.

  1. 死信交换机和死信队列和普通的没有区别
  2. 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
  3. 消息成为死信的三种情况:
    1. 队列消息长度达到限制
    2. 消费者拒绝接受消费信息,并且不重回队列
    3. 原队列在消息过期设置,消息到达超时时间未被消费
延迟队列

延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费.

需求:

  1. 下单后,30分钟未支付,取消订单,回滚库存.
  2. 新用户注册成功7天后,发送短信问候.

实现方式:

  1. 定时器(不好,定时遍历订单时间,耗费资源)

  2. 延迟队列

    订单系统----------> 延迟队列---------(30分钟后)----------->库存系统-----(判断订单1状态)—>支付了,什么都不做,未支付,回滚库存.

    很可惜,在rabbitmq中并未提供延迟队列功能

    但是可以使用:TTL+死信队列组合实现延迟队列的效果.(消费者监听死信队列)

日志与监控
消息可靠性分析与追踪

在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况,对于RabbitMQ而言,可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失.这个时候就需要一个较好的机制跟踪记录消息的投递过程.以此协助开发和运维人员进行问题的定位.

在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪.

消息追踪-Firehose

firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式发送到默认的exchange上.这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic类型的exchange.发送到这个exchange上的消息的routing key为public.exchangename和deliver.queuename.其中exchangename和queuename为实际exchange和queue的名称.分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息.

注意:打开trace会影响消息写入功能,适当打开后请关闭.

​ rabbitmqctl trace_on:开启Firehose命令

​ rabbitmqctl trace_off:关闭Firehose命令

消息追踪-rabbitmqq_tracing

rabbitmq_tracing和Firehose在实现上如出一辙,只不过rabbitmq_tracing的方式比Firehose多了一层GUI的包装,更容易使用和管理.

启用插件:rabbitmq-plugins enable rabbitmq_tracing

管理

RabbitMQ应用问题### RabbitMQ高级特性

消息可靠性投递

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

  • confirm 确认模式
  • return 追回模式

rabbitmq整个消息投递路径为:producer—>exchange—>queue—>consumer

  1. 消息从producer到exchange则会返回一个confirmCallback
  2. 消息从exchange—>queue投递失败则会返回一个returnCallback.

利用这两个callback控制消息的可靠性传递

//配置信息
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-confirms: true
    publisher-returns: true
package com.qf;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.Callable;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProviderApplicationTests {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    public void contextConfirm() {
        /*
         * 确认模式:
         *   步骤:
         *       1.确认模式开启:publisher-confirms: true
         *       2.在rabbitTemplate定义confirmCallback回调函数
         * */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
            * @Author: Mr.Zhou
            * @Date: 2021/3/2 23:31
            * @Description: confirm
            * @param correlationData:相关配置信息
             * @param b: exchange交换机是否成功收到了消息,true代表成功,false代表失败
             * @param s: 当b为false时,s会显示失败原因
            * @Return: void
            */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b){
                    //接收成功
                    System.out.println("消息接收成功");
                }else{
                    //接收失败,处理消息再次发送
                    System.out.println("消息接受失败,失败原因:"+s);
                }
                System.out.println("confirmCallback执行了......");
            }
        });
        //3.发送消息
        rabbitTemplate.convertAndSend("test_exchange","confirm","message confirm ....");
    }
    @Test
    public void contextReturn() {
        /*
        * 回退模式:当消息发送给Exchange后,Exchange路由到Queue失败才会执行ReturnCallback
        *   步骤:
        *       1.开启回退模式  publisher-returns: true
        *       2.设置ReturnCallBack
        *       3.设置Exchange处理消息的模式
        *           1.如果消息路由到queue,则丢弃消息(默认)
        *           2.如果消息没有路由到queue,返回消息发送方ReturnCallBack
        * */
        //设置交换机处理失败消息模式
        rabbitTemplate.setMandatory(true);
        //设置returnCallBack
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
            * @Author: Mr.Zhou
            * @Date: 2021/3/4 11:09
            * @Description: returnedMessage
            * @param message: 消息对象
             * @param i: 错误码
             * @param s: 错误信息
             * @param s1: 交换机
             * @param s2: 路由键
            * @Return: void
            */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("消息对象:"+message);
                System.out.println("错误信息:"+s);
                System.out.println("交换机:"+s1);
                System.out.println("路由键:"+s2);
                System.out.println("return执行了....");
            }
        });
        rabbitTemplate.convertAndSend("test_exchange","confirm1","message confirm ....");
    }
}
Consumer ACK

ack指Acknowledge,确认,表示消费端收到消息后的确认方式.

有三种确认方式:

  • 自动确认:acknowledge=“none”
  • 手动确认:acknowledge=“manual”
  • 根据异常情况确认:acknowledge=“auto”(这种方式使用麻烦)

自动确认是指,当消息一旦被consumer接收到,则自动确认收到,并将相应message从rabbitmq消息缓存中移除,但是实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失,如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息.

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual #开启手动ack
package com.qf.controller;/*
 * Description:
 * User:Hello,Mr.Zhou
 * Date:2021-03-04
 * Time:11:36
 *
 * */

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

import java.io.IOException;

/*
* Consumer ACK机制
*   1.设置手动签收.acknowledge-mode: manual
*   2.让监听器类实现ChannelAwareMessageListener接口
*   3.如果消息成功处理,则调用channel的basicAck()签收
*   4.如果消息处理失败,则调用channel的basicNack()拒绝签收,broker重新发送给consumer
*
* */
@Component
public class AckListenter implements ChannelAwareMessageListener {
    @RabbitListener(queues = "test_queue")
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //1.接受转换消息
            System.out.println(new String(message.getBody()));
            //2.处理业务逻辑
            System.out.println("处理业务逻辑");
            //3.手动签收
//            int i=1/0;
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
//            e.printStackTrace();
            //4.拒绝签收
            /*
            * 第三个参数:requeue:重回队列,如果设置为true,消息重新回到队列,broker会重新发送消息给消费端
            * */
            channel.basicNack(deliveryTag,true,true);
        }
    }
}
  • 在配置文件中设置acknowledge属性,设置ack方式none:自动确认,manual:手动确认
  • 如果在消费端出现异常,则调用channel.basicAck(deliveryTag.false);方法确认签收消息
  • 如果出现异常,则在catch中调用basicNack或basicReject,拒绝接受消息,让mq重新发送消息
消息端限流
  • 配置prefetch属性设置消费端一次拉取多少条消息
  • 消费端的确认模式一定为手动确认.acknowledge=“manual”
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual #开启手动ack
        prefetch: 1 #一次接受一条消息
package com.qf.controller;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
/*
*
* @Author: Mr.Zhou
* @Date: 2021/3/4 20:04
* @Description: consumer 限流机制
* @param null:
* @Return:
*/
/*
* 1.确保ack机制为手动确认.
* 2.listener-container配置属性
*   perfectch = 1,表示消费端每次从mq拉出一条消息来消费,直到确认消费完毕后,才会继续拉下一条消息.
*
* */
@Component
public class QosListenter implements ChannelAwareMessageListener {
    @RabbitListener(queues = "test_queue")
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //1.获取消息
        System.out.println(new String(message.getBody()));
        //2.处理业务
        //3.签收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
    }
}
TTL
  • TTL全称Time To Live (存活时间/过期时间)
  • 当消息到达存活时间后,还没有被消费,会被自动清除
  • RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间

生产者[订单系统] --------> 中间件{30分钟} ------------>消费者[支付系统]

30分钟消息未被消费自动取消

死信队列

死信队列,英文缩写:DLX.Dead Letter Exchange(死信交换机)当消息成为死信后,可以被重新发送到另一个交换机,这个交换机就是DLX.

  1. 死信交换机和死信队列和普通的没有区别
  2. 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
  3. 消息成为死信的三种情况:
    1. 队列消息长度达到限制
    2. 消费者拒绝接受消费信息,并且不重回队列
    3. 原队列在消息过期设置,消息到达超时时间未被消费
延迟队列

延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费.

需求:

  1. 下单后,30分钟未支付,取消订单,回滚库存.
  2. 新用户注册成功7天后,发送短信问候.

实现方式:

  1. 定时器(不好,定时遍历订单时间,耗费资源)

  2. 延迟队列

    订单系统----------> 延迟队列---------(30分钟后)----------->库存系统-----(判断订单1状态)—>支付了,什么都不做,未支付,回滚库存.

    很可惜,在rabbitmq中并未提供延迟队列功能

    但是可以使用:TTL+死信队列组合实现延迟队列的效果.(消费者监听死信队列)

日志与监控
消息可靠性分析与追踪

在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况,对于RabbitMQ而言,可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失.这个时候就需要一个较好的机制跟踪记录消息的投递过程.以此协助开发和运维人员进行问题的定位.

在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪.

消息追踪-Firehose

firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式发送到默认的exchange上.这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic类型的exchange.发送到这个exchange上的消息的routing key为public.exchangename和deliver.queuename.其中exchangename和queuename为实际exchange和queue的名称.分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息.

注意:打开trace会影响消息写入功能,适当打开后请关闭.

​ rabbitmqctl trace_on:开启Firehose命令

​ rabbitmqctl trace_off:关闭Firehose命令

消息追踪-rabbitmqq_tracing

rabbitmq_tracing和Firehose在实现上如出一辙,只不过rabbitmq_tracing的方式比Firehose多了一层GUI的包装,更容易使用和管理.

启用插件:rabbitmq-plugins enable rabbitmq_tracing

管理

RabbitMQ应用问题

1.消息可靠性保障
  • 消息补偿机制

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JFthTcIS-1616037789963)(C:\Users\Hello,Mr.Zhou\Desktop\QQ截图20210317180300.jpg)]

2.消息幂等性处理

幂等性指的是一次和多次请求同一个资源,对于资源本身应该具有同样的结果,也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同.

在mq中指的是,消费多条相同的消息,得到的消费该消息一次相同的结果.

  • 乐观锁解决方案

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4j2LgUm0-1616037789966)(C:\Users\Hello,Mr.Zhou\Desktop\数据库乐观锁保障幂等性.jpg)]

RabbitMQ集群搭建

RabbitMQ高可用集群
1.消息可靠性保障
  • 消息补偿机制

    在这里插入图片描述

2.消息幂等性处理

幂等性指的是一次和多次请求同一个资源,对于资源本身应该具有同样的结果,也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同.

在mq中指的是,消费多条相同的消息,得到的消费该消息一次相同的结果.

  • 乐观锁解决方案

在这里插入图片描述

RabbitMQ集群搭建

RabbitMQ高可用集群
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值