Springboot-Rabbitmq 高级特性

Springboot-Rabbitmq 高级部分

消息可靠性投递

image-20220430082604630

生产者 --> 交换机 : 通过confirmCallback

交换机 --> 队列 : 通过returnCallback

注意: 可靠投递会使rabbitmq吞吐量严重下降,非重要消息,不建议开启

confirmCallback 生产者-> 交换机

生产者消息发送到 Broker 后,返回给生产者一个ACK来确定消息收到。

实现confirmCallBack接口版本

spring.rabbitmq.publisher-returns: true

2.4 以后的配置

spring.rabbitmq.publisher-confirm-type: correlated

代码实现

配置类
@Configuration
public class RabbitMQConfig {

    // 消息可靠性
    public final static String EXCHANGE_TO_EXCHANGE = "reliable_exchange";
    public final static String QUEUE_RELIABLE = "reliable_queue";

    @Bean(EXCHANGE_TO_EXCHANGE)
    public Exchange createExchange(){
        return ExchangeBuilder.topicExchange(EXCHANGE_TO_EXCHANGE).durable(true).build();
    }

    @Bean(QUEUE_RELIABLE)
    public Queue createQueue(){
        return QueueBuilder.durable(QUEUE_RELIABLE).build();
    }

    @Bean
    public Binding binding1(@Qualifier(QUEUE_RELIABLE) Queue queue,
                            @Qualifier(EXCHANGE_TO_EXCHANGE)Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("reliable.*").noargs();
    }

}
测试代码
@Autowired
    private RabbitTemplate template;
    // 消息可靠性投递 生产者到交换机
    @Test
    public void test1() {
        template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 配置
             * @param b  交换机是否收到消息,true 成功
             * @param s  失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("ConfirmCallback==>");
                System.out.println("correlationDate="+correlationData);
                System.out.println("ack=" + b);
                System.out.println("causer="+ s);

                if(b) System.out.println("成功收到消息");
                else System.out.println("消息接收失败");
            }
        });
        template.convertAndSend(RabbitMQConfig.EXCHANGE_TO_EXCHANGE,"reliable.do","测试消息");
    }
结果
# 成功
ConfirmCallback==>
correlationDate=null
ack=true
causer=null
成功收到消息
#失败 这里修改了交换机名 
ConfirmCallback==>
correlationDate=null
ack=false
causer=channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'reliable_exchangess' in vhost '/study', class-id=60, method-id=40)
消息接收失败

returnCallback 交换机-> 队列

  1. 交换机到队列不成功,丢弃消息(默认)
  2. 交换机到队列不成功,返回给消息生产者 returnCallback
#开启 returnCallback
spring.rabbitmq.publisher-returns=true
# 设置失败消息返回 生产者
spring.rabbitmq.template.mandatory=tru
测试代码
// 消息可靠性 交换机 到 队列
    @Test
    void test2() {
        template.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                int replyCode = returnedMessage.getReplyCode();
                System.out.println("code"+replyCode);
                System.out.println("消息==="+ returnedMessage.toString());
            }
        });
        //成功
        //template.convertAndSend(RabbitMQConfig.EXCHANGE_TO_EXCHANGE,"reliable.do","测试交换机到队列");
        //失败 queue 不存在
        template.convertAndSend(RabbitMQConfig.EXCHANGE_TO_EXCHANGE,"x.reliable.do","测试交换机到队列");
    }
结果

成功匹配没有任何返回

# 失败 发送匹配不到key
code312
消息===ReturnedMessage [message=(Body:'测试交换机到队列' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]), replyCode=312, replyText=NO_ROUTE, exchange=reliable_exchange, routingKey=x.reliable.do]

ACK mq -> 消费者

消费者取走消息,处理完成后,队列删除消息

消费者未处理完消息,消息会重新返回队列

消费者返回ack 后mq 才会确认消息被消费

ACK默认是开启的,消息被取走会进入 Unacked 状态

# 手动开启
spring.rabbitmq.listener.simple.acknowledge-mode.manual
package com.example.demo.lestener;

import com.example.demo.config.RabbitMQConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author LFweixiao
 * @create 2022-04-30 11:19 上午
 *
 *  开启 ACK 手动提交  需要去配置文件 开启配置
 */
@Component
public class RabbitMqListener {

    @RabbitListener(queues = {RabbitMQConfig.QUEUE_RELIABLE})
    public void messageHandler(String body, Message message, Channel channel) throws IOException {
        // deliveryTag 投递序号,每次消费消息或者重新投递消息,deliveryTag 都会增加
        long msgTag = message.getMessageProperties().getDeliveryTag();
        System.out.println("消息目标=="+ msgTag);
        System.out.println("message=="+ message.toString());
        System.out.println("body=="+ body);
        //业务逻辑

        // broker 消息拒绝确认  basiNack 可以一次拒绝 0到多个消息拒收 可以设置 requeue
        //channel.basicNack(msgTag,false,true);

        // broker 确认消息
        channel.basicAck(msgTag,false);

        //一次拒收一条消息
        //channel.basicReject(msgTag,true);

    }
}

死信队列+TTL

TTL = time to live消息存活时间

rabbitmq 两种ttl设置

  • 单独消息进行配置ttl
  • 队列设置ttl(推荐)

DLX = Dead Letter Excahnge死信交换机

  • 消费者拒收消息basic.reject/basic.nack
  • 消息在队列中超时未被消费
  • 队列容量满,被淘汰出来的消息(最先进入队列的消息)

消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列

image-20220501092841692

控制台创建 交换机——死信交换机和普通交换机一样

image-20220501102538631

控制台创建 死信队列 ——死信队列和普通队一样

记得绑定交换机

image-20220501102618051

创建普通队列 结合 死信交换机

x-message-ttl对整个队列设置统一的超时时间 毫秒 单位

队列超时时间和 消息超时时间都设置是,时间短的会触发

image-20220501103132610

代码实现创建 ‼️
package com.example.demo.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

/**
 * @author LFweixiao
 * @create 2022-05-01 10:55 上午
 *  配置 死信 队列,交换机
 */
//@Configuration
public class DieMessageConfig {

    //死信队列
    public static final String DEAD_QUEUE = "dead_queue";
    //死信交换机
    public static final String DEAD_EXCHANGE = "dead_exchange";
    //死信队列的 路由key
    public static final String DEAD_ROUTING_KEY = "dead_routing_key";

    @Bean(DieMessageConfig.DEAD_EXCHANGE)
    public Exchange createExchange() {
        return new TopicExchange(DEAD_EXCHANGE,true,false);
    }

    @Bean(DieMessageConfig.DEAD_QUEUE)
    public Queue createQueue() {
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    @Bean
    public Binding deadBinding() {
        return new Binding(DEAD_QUEUE, Binding.DestinationType.QUEUE,
                            DEAD_EXCHANGE,DEAD_ROUTING_KEY,null);
    }


    //设置 普通队列,交换机,  完成普通队列,和死信交换机的绑定

    //普通队列
    public static final String NEW_TEXT_TTL_QUEUE = "new_text_ttl_queue";
    // topic 交换机
    public static final String NEW_TEXT_TTL_EXCHANGE = "new_text_ttl_exchange";
    // 路由key
    public static final String NEW_TEXT_TTL_ROUTING_KEY = "new_text_ttl_routing_key";

    @Bean(DieMessageConfig.NEW_TEXT_TTL_EXCHANGE)
    public Exchange createNewTextTTL() {
        return new TopicExchange(NEW_TEXT_TTL_EXCHANGE,true,false);
    }

    //普通队列绑定 私信交换机
    @Bean(DieMessageConfig.NEW_TEXT_TTL_QUEUE)
    public Queue createQueueNew() {
        HashMap<String, Object> args = new HashMap<>();
        //设置创建参数
        //队列消息过期时间
        args.put("x-message-ttl",10000);
        //消息过期后进入 私信交换机的 routing key
        args.put("x-dead-letter-routing-key",NEW_TEXT_TTL_ROUTING_KEY);
        // 绑定死信交换机
        args.put("x-dead-letter-exchange",NEW_TEXT_TTL_EXCHANGE);

        return  QueueBuilder.durable(NEW_TEXT_TTL_QUEUE).withArguments(args).build();
    }

    @Bean
    public Binding createBindingNew(){
        return new Binding(NEW_TEXT_TTL_QUEUE,Binding.DestinationType.QUEUE,
                            NEW_TEXT_TTL_EXCHANGE,NEW_TEXT_TTL_ROUTING_KEY,null);
    }

}

底部

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值