学习rabbitmq过程中的问题记录

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


RabbitMq问题记录

1、如果memory大小超过了high watermark的大小,会阻塞生产者发送过来的消息

学rabbitmq的过程中,因为买的轻量级服务器,内存比较小,所以手动给mq设置了内存大小,也是百度以后稀里糊涂地设置,然后写了一个product,登录mq的web界面找不到消息,然后发现
Overview下面的Memory 是红色,百度再百度以后,说是RABBITMQ_MEMORY_HIGH_WATERMARK=0.4这个参数默认是0.4…反正就是已经超过high watermark的阈值了,这个参数就是用来设置这个阈值的,要么你把这个阈值调大,要么你运行的内存够大。如果是通过docker启动的容器,可以这样设置
docker run -e RABBITMQ_MEMORY_HIGH_WATERMARK=0.8 -m 512m -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management
另外一种方式就是通过写一个mq的config文件,听说这玩意要手动写,懒得搭理了,因为我是学习过程,所以直接docker rm 再docker run了

2、RabbitListener注解下的方法用的private修饰,消费者启动以后,代码没有消费到消息。

(1)首先我看了一下component注解所在位置,发现springboot扫描到,因为它默认的就是扫描当前包,以及当前包下的子包,然后通过这个验证了一下。很明显是扫描到了

ConfigurableListableBeanFactory beanFactory = SpringApplication.run(Demo1Application.class, args).getBeanFactory();
System.out.println(beanFactory.getBean(Consumer.class));;
       

在这里插入图片描述
(2)然后我又看了一下注解里的队列名,发现没有写错
(3)终极大法,无奈大法,因为我脑子马上想到的是它这里头的方法是从哪到哪,我要是能一层一层的看下去,那我应该就能找到问题所在,但是菜鸡一枚,我的大招是核对我的学习文档,我哪里写的和他不一样,发现RabbitListener这个注解的方法,我用的是private,改成了public以后就ok了,具体为什么我也没整明白

3、明明是direct交换器绑定的队列,但是没有指定routing key,也能正常发送消息

交换器的type:direct 、fanout、topic,讲道理direct交换器是需要指定对应的routing key,才能把消息发送到指定的队列上,我理解的是在创建交换器或者队列的时候,需要手动指定routing key

  @Bean
    protected Queue queue(){
        Queue queue = new Queue("myQueue");
        return queue;
    }

这是我创建的队列,但是没有指定routing key,而且它绑定的是默认的direct类型的交换器。这种情况,会有一个默认的routing key:队列名。所以也能正常路由

总结:mq的基本配置

队列的创建:

  @Bean
    protected Queue queue(){
        Queue queue = new Queue("myQueue");
        return queue;
    }

交换器的创建

 @Bean
    public TopicExchange topicExchange()
    {
        return new TopicExchange("topicEx");
    }
 @Bean
    public FanoutExchange fanoutExchange()
    {
        return new FanoutExchange("fanoutEx");
    }

交换器的绑定

@Bean
    public Binding topicBinding(Queue queue,TopicExchange TopicExchange ){
        return BindingBuilder.bind(queue).to(TopicExchange).with("test.");
    }

在这里插入图片描述
在这里插入图片描述

4、添加好mq的包,写好configuration类以后,启动springboot发现web上没有我配置的交换机、队列

然后加了这个玩意以后就出来了

@Autowired
    private RabbitTemplate rabbitTemplate;

    @RabbitListener(queues = "dead_queue")
    public void test(Message message, Channel channel) throws IOException {
        System.out.println("消息:"+new String(message.getBody()));
        long tag = message.getMessageProperties().getDeliveryTag();
        channel.basicAck(tag,false);
    }

5、新建了一个commons的模块,在commons下面,新建了一个rabbit-config模块,然后把commons添加到了product服务模块,在product模块里启动以后,在web界面没有队列和交换器出现

然后在product模块里尝试声明一下rabbit-config,结果没有代码提示,我本以为我把commons添加到product的依赖里就行了,最后又单独把rabbit-config添加到了product的pom的依赖里

6、如何配置消费消息手动确认

在yml文件里添加

rabbitmq:
    host: ip
    port: 5672
    username: guest
    password: guest
    listener:
      simple:
        acknowledge-mode: manual

在消费者代码里,需要添加,如果不确认就会存在多次消费的情形

channel.basicAck(tag,false);

7、Mq管理界面中queues菜单下的features字段为空,其他的为D

表示它不是持久化的(durable)

8、消息发送开启comfirm机制,以为只有发送失败才会走回调方法,成功也会走的

   rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {  
            @Override  
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {  
                if (ack) {  
                    // 消息成功发送到交换器  
                    System.out.println("消息发送成功: " + correlationData);  
                } else {  
                    // 消息发送失败,比如交换器不存在等  
                    System.out.println("消息发送失败: " + cause);  
                }  
            }  
        });  

9、死信队列的使用

在创建队列的时候,绑定私信交换机就可以了
1、可以给消息指定过期时间,但是我在写的时候,出现了
#method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg ‘x-dead-letter-exchange’ for queue ‘testQueue’ in vhost ‘/’: received the value ‘dead_exchange’ of type ‘longstr’ but current is none, class-id=50, method-id=10)
我前几天是这样写的:

    @Bean
    public Binding bindDeadExchange(Queue deadQueue,FanoutExchange deadExchange){
        return BindingBuilder.bind(deadQueue).to(deadExchange);
    }

我今天又是这样写的:

    @Bean
    public Queue testQueue(){
        return QueueBuilder.nonDurable(RabbitMqConstant.TEST_QUEUE).deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE).build();
    }

已经存在的和将要去创建的存在不一致的地方,然后我删了之前的队列重新写了一个
消息发送:

rabbitTemplate.convertAndSend("testExchange_fanout","", "消息", message -> {
            message.getMessageProperties().setExpiration("10000");
            return message;
        });

2、或者给队列设置时间

  @Bean
    public Queue orderQueue(){
        return QueueBuilder.nonDurable("orderQueue").deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE).ttl(10000).build();
    }

发送消息的时候:

rabbitTemplate.convertAndSend("testExchange_fanout","", "消息");

3、添加mq插件,使用延迟交换机
没有尝试

1、2的缺陷就是时间是固定的,mq的队列是FIFO原则,如果你给不同消息设置不同的时间,就会影响进入死信队列的消息,导致它们会一致卡在原来的队列里

10、如何路由多个死信队列

业务:消息先进入 orderqueue,过期以后进入deadQueue1和deadQueue2这两个队列。这两个死信队列关联一个交换机就行了,注意到的点就是,发送到orderQueue的时候,routing key也是路由到死信队列的routing key

@Configuration
public class RabbitMqConfig {

    @Bean
    public Queue orderQueue(){
        return QueueBuilder.nonDurable("orderQueue").deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE_DIRECT).ttl(10000).build();
    }

    @Bean
    public DirectExchange deadDirectExchange(){
        return ExchangeBuilder.directExchange(RabbitMqConstant.DEAD_EXCHANGE_DIRECT).build();
    }

    @Bean
    public FanoutExchange fanoutExchange(){
        return ExchangeBuilder.fanoutExchange(RabbitMqConstant.TEST_FANOUT_EXCHANGE).build();
    }

    @Bean
    public Queue deadQueue1(){
        return QueueBuilder.durable(RabbitMqConstant.DEAD_QUEUE).build();
    }

    @Bean
    public Queue deadQueue2(){
        return QueueBuilder.durable(RabbitMqConstant.DEAD_QUEUE2).build();
    }


    @Bean
    public Binding bindDeadExchange(Queue deadQueue1,Exchange deadDirectExchange){
        return BindingBuilder.bind(deadQueue1).to(deadDirectExchange).with("a").noargs();
    }

    @Bean
    public Binding bindDeadExchange2(Queue deadQueue2,Exchange deadDirectExchange){
        return BindingBuilder.bind(deadQueue2).to(deadDirectExchange).with("b").noargs();
    }

    @Bean
    public Binding bindOrderQueueToEx(Queue orderQueue,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(orderQueue).to(fanoutExchange);
    }

}

发送消息观察现象:

rabbitTemplate.convertAndSend("testExchange_fanout","a", "消息");
rabbitTemplate.convertAndSend("testExchange_fanout","b", "消息");

11、消息被手动确认的时候,deliveryTag有这么个东东,但是网上又说它是重复的

不同的消费者之间这个值确实存在重复的,但是消费者从队列里消费到消息,是基于channel,而每一个经过channel的message的deliveryTag是不会重复的。相当于队列里的消费分组以后id唯一

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值