rabbitMQ学习记录

分布式系统通信的两种方式: 1 远程调用,2 借助消息队列

消息队列的优势: 1 应用解耦: 情况1:如果A系统直接调B,C系统,如果B/C系统报错,对A系统也有影响;情况2:如果要加个D系统,对于直接调用的方式需要修改A系统的代码,对于用消息队列的方式不用改A系统代码,D系统直接从消息队列取消息就行.

2:削峰填谷:MQ能处理的最大并发数远高于应用程序,可以将请求保存到MQ中,让消费者慢慢消费.

消息队列的劣势: 1 引入外部依赖,系统可靠性降低 2 复杂度上升,如何保证消费的顺序性,消息不丢失,不重复消费,消息一致性

总结:什么情况下适合用MQ: 1 A系统调B系统,如果需要B系统的反馈,则不能用MQ. 2 

 MQ工作模式

  * 队列模式:

 多个消费者之间是竞争关系,一条消息只能被一个消费者消费

队列模式代码就是在简单模式基础上再启动一个消费者

 * 订阅模式:

 

 生产者代码:

复制代码
package com.seeyii.web.yk.pubSub;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168.xx.xx");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername("username"); //默认值 guest
        factory.setPassword("password"); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 创建交换机
        //String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map<String, Object> arguments
       /* exchange: 交换机名
          type:交换机类型: direct(定向),fanout(广播,向所有绑定的队列发消息),topic(通配符方式),headers(参数匹配)
          durable: 持久化
          autoDelete: 自动删除
          arguments:参数
           */
       String exchange_name = "fanout_exchange";
        channel.exchangeDeclare(exchange_name, BuiltinExchangeType.FANOUT,true,false,null);
        //channel和交换机绑定
        // 5 创建两个队列
        String queue_name1="fanout_queue1";
        String queue_name2="fanout_queue2";
        /*
          queue: 对列名,
          durable:是否持久化,当mq重启后是否还在
          exclusive:是否排他,
            * 如果排他,只能有一个消费者监听这个队列,exclusive为true时,concurrency必须是1,concurrency是消费者创建几个线程去消费。
* 当连接关闭时是否删除队列 autoDelete:当没有连接时,是否自动删除 arguments: 参数 **/ //如果没有这个名字的队列则会创建,如果有就不创建 channel.queueDeclare(queue_name1,true,false,false,null); channel.queueDeclare(queue_name2,true,false,false,null); //绑定交换机和队列 //String queue, String exchange, String routingKey /*queue:对列名 exchange:交换机名 routingKey:路由键名 */ channel.queueBind(queue_name1,exchange_name,"");//如果交换机类型是广播,路由键就是空串 channel.queueBind(queue_name2,exchange_name,""); //6 发消息 /* exchange:交换机名称,简单模式下默认空串 routingKey:路由键,简单模式下就是channel名 BasicProperties props: 配置信息, byte[] body:发送的消息数据 **/ String body="hello,rabbitmq+++++"; channel.basicPublish("","hello_world",null,body.getBytes()); //7 释放资源 if(channel!=null){ channel.close(); } if(connection!=null){ connection.close(); } } }
复制代码

消费者1代码:

复制代码
package com.seeyii.web.yk.pubSub;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168..");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername(""); //默认值 guest
        factory.setPassword(""); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 5 创建队列,生产者已经创建过队列,消费者不用再创建
       //6 接收消息
        /*
      String queue:对列名称,
      boolean autoAck:是否自动确认,
      Consumer callback:回调对象
         **/
        Consumer consumer=new DefaultConsumer(channel){
            //回调方法: 当收到消息后自动执行该方法
            /*consumerTag:消息标识
            envelope获取交换机,路由键的信息
            properties:配置信息
            body: 消息*/
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("envelope-Exchange:"+envelope.getExchange());
//                System.out.println("envelope-RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("消费者1收到消息: body:"+new String(body));
            }
        };
        channel.basicConsume("fanout_queue1",true,consumer);
        //消费者不要关闭连接资源
    }
}
复制代码

消费者2代码:

复制代码
package com.seeyii.web.yk.pubSub;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168..");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername(""); //默认值 guest
        factory.setPassword(""); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 5 创建队列,生产者已经创建过队列,消费者不用再创建//6 接收消息
        /*
      String queue:对列名称,
      boolean autoAck:是否自动确认,
      Consumer callback:回调对象
         **/
        Consumer consumer=new DefaultConsumer(channel){
            //回调方法: 当收到消息后自动执行该方法
            /*consumerTag:消息标识
            envelope获取交换机,路由键的信息
            properties:配置信息
            body: 消息*/
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("envelope-Exchange:"+envelope.getExchange());
//                System.out.println("envelope-RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("消费者2收到消息: body:"+new String(body));
            }
        };
        channel.basicConsume("fanout_queue2",true,consumer);
        //消费者不要关闭连接资源
    }
}
复制代码

 

路由模式:

 

 

生产者代码:

复制代码
package com.seeyii.web.yk.routingKey;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168..");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername(""); //默认值 guest
        factory.setPassword(""); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 创建交换机
        //String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map<String, Object> arguments
       /* exchange: 交换机名
          type:交换机类型: direct(定向),fanout(广播,向所有绑定的队列发消息),topic(通配符方式),headers(参数匹配)
          durable: 持久化
          autoDelete: 自动删除
          arguments:参数
           */
       String exchange_name = "direct_exchange";
        channel.exchangeDeclare(exchange_name, BuiltinExchangeType.DIRECT,true,false,null);
        //channel和交换机绑定
        // 5 创建两个队列
        String queue_name1="direct_queue1";
        String queue_name2="direct_queue2";
        /*
          queue: 对列名,
          durable:是否持久化,当mq重启后是否还在
          exclusive:是否排他,
            * 如果排他,只能有一个消费者监听这个队列
            * 当连接关闭时是否删除队列
           autoDelete:当没有连接时,是否自动删除
           arguments: 参数
         **/
        //如果没有这个名字的队列则会创建,如果有就不创建
        channel.queueDeclare(queue_name1,true,false,false,null);
        channel.queueDeclare(queue_name2,true,false,false,null);
        //绑定交换机和队列
        //String queue, String exchange, String routingKey
        /*queue:对列名
          exchange:交换机名
          routingKey:路由键名
          */
        //队列1绑定1个路由键,队列2绑定三个路由键
        channel.queueBind(queue_name1,exchange_name,"error");
        channel.queueBind(queue_name2,exchange_name,"info");
        channel.queueBind(queue_name2,exchange_name,"warning");
        channel.queueBind(queue_name2,exchange_name,"error");
        //6 发消息
        /*
      exchange:交换机名称,简单模式下默认空串
      routingKey:路由键,简单模式下就是channel名
      BasicProperties props: 配置信息,
      byte[] body:发送的消息数据
         **/
        String body="向direct类型交换机发数据,routingKey是info,交换机绑定两个队列";
        channel.basicPublish(exchange_name,"info",null,body.getBytes());
        //7 释放资源
        if(channel!=null){
            channel.close();
        }
        if(connection!=null){
            connection.close();
        }
    }
}
复制代码

消费者代码:

复制代码
package com.seeyii.web.yk.routingKey;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168..");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername(""); //默认值 guest
        factory.setPassword(""); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 5 创建队列,生产者已经创建过队列,消费者不用再创建
        /*
          queue: 对列名,
          durable:是否持久化,当mq重启后是否还在
          exclusive:是否排他,
            * 如果排他,只能有一个消费者监听这个队列
            * 当连接关闭时是否删除队列
           autoDelete:当没有连接时,是否自动删除
           arguments: 参数
         **/
        //如果没有这个名字的队列则会创建,如果有就不创建
       // channel.queueDeclare("hello_world",true,false,false,null);

        //6 接收消息
        /*
      String queue:对列名称,
      boolean autoAck:是否自动确认,
      Consumer callback:回调对象
         **/
        Consumer consumer=new DefaultConsumer(channel){
            //回调方法: 当收到消息后自动执行该方法
            /*consumerTag:消息标识
            envelope获取交换机,路由键的信息
            properties:配置信息
            body: 消息*/
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                System.out.println("消费者1收到消息: body:"+new String(body));
            }
        };
        channel.basicConsume("direct_queue1",true,consumer);
        //消费者不要关闭连接资源
    }
}
复制代码
复制代码
package com.seeyii.web.yk.routingKey;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @PackageName:com.seeyii.web.yk
 * @Description:
 * @Author 杨坤
 * @Date:2022/6/28 19:38
 */
public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2 设置参数
        factory.setHost("192.168..");// ip,默认localhost
        factory.setPort(5672);//端口,默认5672
        factory.setVirtualHost("/yk");//虚拟机,默认值 /
        factory.setUsername(""); //默认值 guest
        factory.setPassword(""); //默认值 guest
        //3 创建连接
        Connection connection = factory.newConnection();
        //4 创建channel
        Channel channel = connection.createChannel();
        // 5 创建队列,生产者已经创建过队列,消费者不用再创建
        /*
          queue: 对列名,
          durable:是否持久化,当mq重启后是否还在
          exclusive:是否排他,
            * 如果排他,只能有一个消费者监听这个队列
            * 当连接关闭时是否删除队列
           autoDelete:当没有连接时,是否自动删除
           arguments: 参数
         **/
        //如果没有这个名字的队列则会创建,如果有就不创建
       // channel.queueDeclare("hello_world",true,false,false,null);

        //6 接收消息
        /*
      String queue:对列名称,
      boolean autoAck:是否自动确认,
      Consumer callback:回调对象
         **/
        Consumer consumer=new DefaultConsumer(channel){
            //回调方法: 当收到消息后自动执行该方法
            /*consumerTag:消息标识
            envelope获取交换机,路由键的信息
            properties:配置信息
            body: 消息*/
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                System.out.println("消费者2收到消息: body:"+new String(body));
            }
        };
        channel.basicConsume("direct_queue2",true,consumer);
        //消费者不要关闭连接资源
    }
}
复制代码

主题模式:

routingKey里使用通配符,*代表一个单词,#代表0或多个单词,代码和发布订阅模式基本相同

 Springboot整合rabbitMQ代码:

配置文件:

spring:
    rabbitmq:
      host: 192.168.xx.xx
      port: 5672
      username: xx
      password: xx

配置类:

复制代码
package com.seeyii.yk.boot;


import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class BootConfig {
    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){
        return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
    }
}
复制代码

生产者:

复制代码
package com.seeyii.yk.boot;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/bootProducer")
public class ProducerBoot {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @PostMapping("/testSendMQ")
    public String testSendMQ() throws InterruptedException {
        rabbitTemplate.convertAndSend(BootConfig.EXCHANGE_NAME,"boot.haha","boot,MQ,hello");
        return "success";
    }
}
复制代码

消费者:

复制代码
package com.seeyii.yk.boot;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class ConsumerBoot {
    @RabbitListener(queues = BootConfig.QUEUE_NAME)
    public void ListenerQueue(Message message){
        System.out.println("收到消息: "+new String(message.getBody()));
    }
}
复制代码

rabbitMQ如何保证消息可靠投递:

rabbitMQ整个消息投递路径为: producer->exchange->queue->consumer

消息从producer到exchange会返回一个ConfirmCallback

消息从exchange到queue会返回returnCallback(经验来看一般returnCallBack出错都是routingkey配错了,只要打印routingKey就好),利用这两个callback保证消息可靠性

自定义连接工厂,开启这两个回调函数

复制代码
 @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(userName);
        connectionFactory.setPassword(passWord);
        connectionFactory.setVirtualHost("/");
//ConfirmCallback设置 connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
//returnCallback设置 connectionFactory.setPublisherReturns(
true); return connectionFactory; }
复制代码

ConfirmCallback和returnCallback:

复制代码
package com.seeyii.yk.boot;

import com.rabbitmq.client.ConfirmCallback;
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.context.ApplicationContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


@RestController
@RequestMapping("/bootProducer")
public class ProducerBoot {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    ApplicationContext applicationContext;
    @PostMapping("/testConfirm")
    public String testConfirm() throws InterruptedException {
        // 1 在连接工厂里开启Confirm
        //2 定义回调函数
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /*CorrelationData: 相关配置信息
            ack:交换机是否成功收到信息
            cause: 失败原因*/
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("confirm方法执行了");
                if(b){
                    //接收成功
                    System.out.println("接收成功:"+s);
                }else{
                    //接收失败
                    System.out.println("接收失败:"+s);
                    //让消息再次发送
                }
            }
        });
        rabbitTemplate.convertAndSend(BootConfig.EXCHANGE_NAME,"boot.haha","boot,MQ,hello");
        return "success";
    }
    @PostMapping("/testCallback")
    public String testCallback() throws InterruptedException {
        //1 连接工厂里设置开启Callback
        //2 设置交换机处理失败消息的模式
        rabbitTemplate.setMandatory(true);
        //3 定义回调函数
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /*message:消息对象
            replayCode:错误码
            replayText:错误信息
            exchange:交换机
            routingKey:路由键*/
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("return callback执行了");
                System.out.println(message);
                System.out.println(i);
                System.out.println(s);
                System.out.println(s1);
                System.out.println(s2);
            }
        });
        rabbitTemplate.convertAndSend(BootConfig.EXCHANGE_NAME,"boott.haha","boot,MQ,hello");
        return "success";
    }
}
复制代码

消费端收到消息的确认方式: ack

自动ack: acknowledge=none ,消费者收到消息后不管后面的处理逻辑是否成功都会给brocker发确认消息

手动ack:acknowledge=manual,代码控制何时发确认消息,可以在业务逻辑完成后再发

如果设置了手动ack,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息.

消费者代码:

复制代码
package com.seeyii.yk.boot;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;


@Component
public class ConsumerBoot {
    @RabbitListener(queues = BootConfig.QUEUE_NAME,ackMode = "MANUAL",concurrency = "4")
    public void ListenerQueue(Message message, String data, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {
        System.out.println("收到消息: "+new String(message.getBody()));
        System.out.println("data: " + data);
        if (data.contains("boot")) {
            // RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
            channel.basicAck(deliveryTag, false);
        } else {
            // 第三个参数true,表示这个消息会重新进入队列
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
复制代码

总结:如何保证消息可靠性

1 持久化,包括消息持久化,交换机持久化,队列持久化

2 生产方确认confirmCallback,returnCallback,消费方手动ack

3 broker集群高可用

消费端限流:

1 配置文件中配置  RabbitListenerContainerFactory

 @Bean
   public RabbitListenerContainerFactory rabbitListenerContainerFactory2(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPrefetchCount(2);//表示消费者每次从mq中拉取两条消息消费,直到手动确认消费完毕后,才会再拉取消息
        return factory;
    }

2 消费者 @RabbitListener 注解里指定containerFactory,并设置手动ack

复制代码
package com.seeyii.yk.boot;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;


@Component
public class ConsumerBoot {
    @RabbitListener(queues = BootConfig.QUEUE_NAME,ackMode = "MANUAL",concurrency = "2",containerFactory = "rabbitListenerContainerFactory2")
    public void ListenerQueue(Message message, String data, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {
        System.out.println("收到消息: "+new String(message.getBody()));
        System.out.println("deliveryTag: " + deliveryTag);
        System.out.println("data: " + data);
        if (data.contains("boot")) {
            System.out.println("业务逻辑处理完成");
            // RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
            channel.basicAck(deliveryTag, false);
        } else {
            // 第三个参数true,表示这个消息会重新进入队列
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
复制代码

TTL:过期时间,可以给队列设置过期时间,到期后队列里所有消息会被清空,也可以单独

设置消息过期时间

 MessageProperties messageProperties = new MessageProperties();
        //设置消息的过期时间3s
        messageProperties.setExpiration("3000");
        Message message = new Message("boot,hello".getBytes(),messageProperties);
        rabbitTemplate.convertAndSend(BootConfig.EXCHANGE_NAME,"boot.haha",message);

设置队列过期时间

 @Bean("bootQueue")
    public Queue bootQueue(){
        return QueueBuilder.durable(QUEUE_NAME).ttl(5000).build();
    }

 

死信队列:

消息成为死信的三种情况: 1:队列长度或数据大小超过限制.2消费者拒绝接收消息(basicNack),并且不把消息重新放回原目标队列.3 队列设置了超时时间,消息达到时间未被消费 

设置死信队列代码

复制代码
// 1 创建死信交换机
    @Bean("deadExchange")
    public Exchange deadExchange(){
        return ExchangeBuilder.topicExchange("dead_exchange").durable(true).build();
    }
// 3 死信队列和死信交换机绑定
    @Bean
    public Binding deadBindQueueExchange(@Qualifier("deadQueue") Queue queue, @Qualifier("deadExchange") Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("dead.#").noargs();
    }
// 2 创建队列,并绑定死信交换机和路由键
    @Bean("bootQueue")
    public Queue bootQueue(){
        return QueueBuilder.durable(QUEUE_NAME).deadLetterExchange("dead_exchange").deadLetterRoutingKey("dead.test").build();
    }
复制代码

队列过期时间(TTL)+死信队列实现延时队列

 消息队列如何解决消息积压:消息积压基本都是因为消费者消费的慢导致的,基本不可能是生产者和mq本身问题,除非大促这种短时间内突然生产出大量消息,在程序设计之初就要保证消费者消费速度比生产者速度快,如果消息积压,排查生产者代码的方向:

是否在某个资源上卡住,是否有死锁,是否因为消息消费失败导致重复消费。解决方法还是先对消费者扩容,再排查代码。             

posted @   杨吃羊  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示