RabbitMQ学习笔记(高级篇)

RabbitMQ学习笔记(高级篇)

文章目录

  • RabbitMQ学习笔记(高级篇)
    • RabbitMQ的高级特性
      • 消息的可靠投递
        • 生产者确认 —— confirm确认模式
        • 生产者确认 —— return确认模式
      • 消费者确认 Consumer ACK
      • 消费端限流
      • TTL
      • 死信队列
      • 延迟队列
      • 日志与监控
      • 消息追踪
    • RabbitMQ的应用问题
      • 消息可靠性保障
      • 消息幂等性保障

RabbitMQ的高级特性

消息的可靠投递

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

    • confirm确认模式
    • return退回模式
  • rabbitmg整个消息投递的路径为:
    producer—>rabbitmq broker—>exchange—>queue—>consumer

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

生产者确认 —— confirm确认模式
  • 消息从producer到exchange则会返回一个confirmCallback。
  • 需要在配置文件中添加 publisher-confirm-type: correlated # 如果不加这个,confirm回调方法无法执行
spring:
  rabbitmq:
    port: 5672
    host: 192.168.253.129
    virtual-host: /domo
    username: lmx
    password: 123456
    publisher-confirm-type: correlated # 如果不加这个,confirm回调方法无法执行
@Test
    void contextLoads() {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("回调方法被执行======");

                if (b){
                    System.out.println("接受信息成功"+s);
                }else {
                    System.out.println("失败原因是"+s);
                }
            }
        });
        String s="waring级别的数据";
        rabbitTemplate.convertAndSend(RabbitMQConfigure.TocpicExchangeName,"serve.order.warning",s);
//        rabbitTemplate.convertAndSend("12","serve.order.warning",s);



  • 函数参数说明:confirm(CorrelationData correlationData, boolean b, String s)
    • b:消息是否发送成功
    • s:发送失败的原因,发送成功为null
    • correlationData:额外添加的参数
生产者确认 —— return确认模式
  • 消息从exchange->queue投递失败则会返回一个returnCallback
  • 需要配置publisher-returns: true属性
spring:
  rabbitmq:
    port: 5672
    host: 192.168.253.129
    virtual-host: /domo
    username: lmx
    password: 123456
    publisher-confirm-type: correlated # 如果不加这个,confirm回调方法无法执行
    publisher-returns: true

@Test
    void contextLoadsRunturnCallback() {
       rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
           @Override
           public void returnedMessage(ReturnedMessage returnedMessage) {
               System.out.println("returncallback方法被执行");
               System.out.println("信息是"+returnedMessage.getMessage());
//               System.out.println(new String(returnedMessage.getMessage().getBody()));
               System.out.println("交换机名称:"+returnedMessage.getExchange());
               System.out.println("路由key民称:"+returnedMessage.getRoutingKey());
               System.out.println("编码:"+returnedMessage.getReplyCode());
               System.out.println("编码信息:"+returnedMessage.getReplyText());
           }
       });
        String s="error级别的数据";
        rabbitTemplate.convertAndSend(RabbitMQConfigure.TocpicExchangeName,"serve.order.error",s);
//        rabbitTemplate.convertAndSend("12","serve.order.warning",s);



    }

RabbitMQ学习笔记(高级篇)_第1张图片

消费者确认 Consumer ACK

  • ack指Acknowledge,确认。表示消费端收到消息后的确认方式。
  • 有三种确认方式:
    • 自动确认:acknowledge="none
    • 手动确认:acknowledge=“manual”
    • 根据异常情况确认:acknowledge=“auto”,(这种方式使用麻烦,不作讲解)
  • 其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应message从RabbitMQ的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。
  • 如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用**channel…basicNack()**方法,让其自动重新发送消息。
spring:
  rabbitmq:
    port: 5672
    host: 192.168.253.129
    virtual-host: /domo
    username: lmx
    password: 123456
    listener:
       direct: # 就是pub、sub模式
        acknowledge-mode: manual
        prefetch: 1000
      simple: # 简单模式与workquene模式
        acknowledge-mode: manual
        prefetch: 1000
@RabbitListener(queues = "quneq1")
    public void Lister(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("error级别的消息是" + new String(message.getBody()));
//            System.out.println("交换机" + message.getMessageProperties().getReceivedExchange());
//            System.out.println("路由信息" + message.getMessageProperties().getReceivedRoutingKey());
            System.out.println("============================");
            System.out.println("处理业务逻辑");
//            int a=1/0;

            channel.basicAck(deliveryTag,true);

        }catch (Exception e){
            e.printStackTrace();
            channel.basicNack(deliveryTag,true,true);
 
    }



  • 在rabbit:listener-container标签中设置acknowledge属性,设置ack方式none:自动确认,manual:手动确认
  • 如果在消费端没有出现异常,则调用channel.basicAck(deliveryTag,false);方法确认签收消息
  • 如果出现异常,则在catch中调用basicNack或basicReject,拒绝消息,让MQ重新发送消息。
  • channel.basicAck(deliveryTag,false)参数:第一个就是message对象中的deliveryTag ,消息的标识符,第二个参数就是是否接收消息
  • basicNack(long deliveryTag, boolean multiple, boolean requeue)
    • 第一个参数message对象中的deliveryTag,第二个参数,true的时候不接收消息,第三个参数,是否将消息恢复到Rabbitmq的消息队列中,重复发送,直到异常处理结束

消费端限流

  • 配置prefetch属性设置消费端一次拉去多少消息
  • 注意:在限流的时候需要将消费端的消费确认模式改为manel(手动确认),否则,限流不生效
spring:
  rabbitmq:
    port: 5672
    host: 192.168.253.129
    virtual-host: /domo
    username: lmx
    password: 123456
    listener:
      direct: # 就是pub、sub模式
        acknowledge-mode: manual
        prefetch: 1 #  每次拉去一条消息
    
      simple: # 简单模式与workquene模式
        acknowledge-mode: manual
        prefetch: 1  #  每次拉去一条消息
@RabbitListener(queues = "quneq2")
    public void Lister2(Message message, Channel channel) throws IOException, InterruptedException {

        System.out.println("info或waring级别的消息是"+new String(message.getBody()));
//        System.out.println("交换机"+message.getMessageProperties().getReceivedExchange());
//        System.out.println("路由信息"+message.getMessageProperties().getReceivedRoutingKey());
//        System.out.println("============================");

        Thread.sleep(1000);
        System.out.println("处理业务逻辑");

//        签收消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);



    }


TTL

  • TTL全称Time To Live(存活时间/过期时间)。

  • 当消息到达存活时间后,还没有被消费,会被自动清除。

  • RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
    RabbitMQ学习笔记(高级篇)_第2张图片
    RabbitMQ学习笔记(高级篇)_第3张图片

  • 向队列添加过期时间

    @Bean
    public Queue ttlqueue(){
//        设置队列过期时间
        return QueueBuilder.durable(ttlqueue).ttl(5000).build();
    }

  • 向单独的消息添加过期时间
    @Test
    public void TestTTl() {
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {

//                设置Expiration属性
                message.getMessageProperties().setExpiration("5000");
                return message;

            }
        };
        rabbitTemplate.convertAndSend(RabbitMQConfigure.TocpicExchangeName, "123.ttl", "单独过期消息发送".getBytes(StandardCharsets.UTF_8),
                messagePostProcessor);
    }

死信队列

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

RabbitMQ学习笔记(高级篇)_第4张图片

  • 消息成为死信的三种情况:
    • 1.队列消息长度到达限制
    • 2.消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false
    • 3.原队列存在消息过期设置,消息到达超时时间未被消费:
  • 队列绑定死信交换机
    • 给队列设置参数:x-dead-letter-exchange和X-dead-letter-routing-key
    • RabbitMQ学习笔记(高级篇)_第5张图片
  • 定义死信交换机,死信队列(同正常交换机,队列相同)
    //    设置死信交换机
    @Bean
    public Exchange dlxexchange() {

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

    @Bean
    public Queue dlxqueue() {

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

    @Bean
    public Binding dlxbinding() {
        return BindingBuilder.bind(dlxqueue()).to(dlxexchange()).with("dlx.#").and(null);
    }

  • 将正常队列与死信队列绑定
@Bean
    public Queue dlxtestqueue() {


        return QueueBuilder.durable(dlxtestqueue).deadLetterExchange(dlxexchange).deadLetterRoutingKey("dlx.123").maxLength(10).build();
    }

    @Bean
    public Binding Exchangedlxtest(){
        return  BindingBuilder.bind(dlxtestqueue()).to(TocpicExchange()).with("testdlx.#").and(null);
    }

延迟队列

RabbitMQ学习笔记(高级篇)_第6张图片
RabbitMQ学习笔记(高级篇)_第7张图片

  • 配置deadLetterExchange(dlxexchange).ttl(10000).deadLetterRoutingKey(“dlx.yanchi”),属性
//    延迟队列,设置队列的过期时间是10秒
    @Bean
    public Queue yanchiqueue(){
        return QueueBuilder.durable("yanchiqueue").deadLetterExchange(dlxexchange).ttl(10000).deadLetterRoutingKey("dlx.yanchi").build();
    }

    @Bean
    public Binding yanchibing(){
        return BindingBuilder.bind(yanchiqueue()).to(TocpicExchange()).with("yanchi.#").and(null);
    }

@Test
    public void errorTestYanchi2() throws InterruptedException {
        LocalDateTime now = LocalDateTime.now();
        rabbitTemplate.convertAndSend(RabbitMQConfigure.TocpicExchangeName, "yanchi.123", "商品订单2" + now);

        for (int i=0;i<10;i++){
            Thread.sleep(1000);
            System.out.println(i+"秒----");
        }
    }

日志与监控

  • 日志文件存放在 /var/log/rabbitmq下面,可以查看
  • 通过web管控台进行监控
    RabbitMQ学习笔记(高级篇)_第8张图片

消息追踪

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

RabbitMQ的应用问题

消息可靠性保障

RabbitMQ学习笔记(高级篇)_第11张图片

消息幂等性保障

  • 幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
  • 在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果。
    RabbitMQ学习笔记(高级篇)_第12张图片

你可能感兴趣的:(java-rabbitmq,rabbitmq,学习)