RabbitMQ:消息中间件

本文介绍了RabbitMQ的概念和管理界面,详细讲解了如何创建队列和交换机,以及Direct、Fanout、Topic和Headers四种交换器的工作原理。同时,讨论了消息传递的同步与异步、生产者确认、消息消费确认以及死信队列的配置。此外,还涵盖了如何处理面试中常问的关于RabbitMQ的问题,如信道的作用、消息的可靠性和幂等性等。
摘要由CSDN通过智能技术生成

概念

在微服务架构中项目之间项目A调用项目B 项目B调用项目C项目C调用项目D。。
用户必须等待项目之间内容依次的运行结束后才会给用户返回结果
这是一个同步的调用,用户等待时间可能比较长,用户体验度比较差

消息中间件: 可以理解成一个队列,把用户需要处理的任务交给放到队列,任务在队列中进行排队等待执行,用户无需等待代码的执行的时间.
在这里插入图片描述

管理界面简介

1.Overview: 此面板为RabbitMQ基础信息展示面板,列举了服务器的信息,如:节点名称、内存占用、磁盘占用等。
2.Connections: 此面板中展示所有连接到RabbitMQ的客户端链接。只展示基于5672端口的链接。
3.Channels: 此面板中展示各链接中的具体信道。标记方式为链接(编号),如:192.168.91.1:12345(1)。
4.Exchanges: 此面板中展示RabbitMQ中已有的交换器,并注明交换器名称、类型等基本信息。其中只有direct交换器有默认交换器(AMQP default),当使用direct交换器时,如果没有明确指定名称,使用AMQP default交换器,也可明确指定名称。但是其他类型交换器没有默认的,都需要指定名称。
5.Queues: 此面板展示RabbitMQ中的队列信息。

创建队列

一.type:
1.Classic队列适用于简单的消息传递
2.Quorum队列提供高可用性和数据持久性,适用于关键应用
3.Stream队列用于处理事件流数据,适用于大规模的事件处理。

二.name: 自定义

三.Durability:
Durable: 消息保存到硬盘,这样消息的安全性更高。重启系统依然队列中数据依然存在
Transient: 消息或队列不会在系统重启后保留,可能会丢失。

四.Auto delete: 是,则所有的消费者都断开连接后删除自己

五.Arguments:
1.x-message-ttl:设置消息的过期时间(以毫秒为单位)。消息如果在指定的时间内没有被消费者消费,将被自动删除。
2.x-max-length:设置队列的最大长度。一旦队列中的消息数量达到该值,新的消息将被拒绝或丢弃,具体行为取决于其他参数的配置。

创建交换机

1.Name: 自定义
2.Type:
Direct Exchange(直接交换机):将消息通过精确的路由键发送到与路由键完全匹配的队列。适合点对点通信,路由键与队列名一一对应。

Fanout Exchange(扇出交换机):
将消息广播到绑定到它的所有队列,忽略路由键。用于广播消息给多个消费者,无需关心路由键。

Topic Exchange(主题交换机):
使用通配符匹配路由键,允许更灵活的消息路由。适合根据多个条件过滤消息,支持复杂的消息匹配。

Headers Exchange(头交换机):
根据消息头的键值对进行匹配,而不是路由键。用于复杂的消息过滤,根据消息的属性进行匹配。

3.autodelete:参数用于设置 RabbitMQ 交换机的自动删除行为:
autodelete=true:如果没有队列与交换机绑定,也没有活跃的连接或通道在使用该交换机时,它会自动删除。
autodelete=false(默认值):交换机不会自动删除,即使没有队列与其绑定或没有活跃的连接使用它。通常在需要长期存在的交换机上使用。

4.Internal的意思是内部的意思,在交换机这里设置为“Yes”之后,表示当前Exchange是RabbitMQ内部使用,用户所创建的Queue不会消费该类型交换机下的消息,既然是为了RabbitMQ系统所用,作为用户,我们就没有必要创建该类型的Exchange,当然默认也是选择No.

5.Arguments:
alternate-exchange:指定备用交换机,用于处理无法路由的消息。
x-delayed-type:创建延迟交换机,用于处理延迟消息。
x-dead-letter-exchange:定义死信消息应该发送到的交换机。
x-dead-letter-routing-key:定义死信消息的路由键。
x-max-priority:启用消息优先级,设置队列支持的最大优先级。

4种常见交换器类型

1.Direct交换器:

direct会根据路由键把消息放入到指定的队列中。
在Queues界面创建队列q1,在Exchanges创建direct交换机fs.direct绑定交换机q1,指定路由key,fs.q1
一.生产者
1.导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2.配置yml

# 配置RabbitMQ相关信息
# 当创建RabbitMQ容器的时候,不提供用户名和密码配置,自动创建用户guest,密码guest。
# guest用户只能本地访问RabbitMQ。
spring:
  rabbitmq:
    host: localhost # RabbitMQ服务器的IP。默认localhost
    port: 32769 # RabbitMQ服务器的端口。
    username: guest # RabbitMQ的访问用户名。默认guest。
    password: guest # RabbitMQ的访问密码。默认guest
    # 注意生产者一定要配置这个,消费者不用
    virtual-host: / # 连接RabbitMQ中的哪一个虚拟主机。默认 /

3.在测试包下发送消息:
类型可以是: AmqpTemplate(顶级接口), RabbitOperations(专用子接口),RabbitTemplate(具体实现)
建议使用接口: 优先级是 RabbitOperations > AmqpTemplate

Spring AMQP可以发送的消息类型必须是Message类型。
Spring AMQP可以帮助程序员自动封装消息类型Message对象(默认封装),自动转换封装的消息体类型是Object,只要类型可序列化即可。

消息发送到队列后,无需等待,是异步操作,生产者接着往下执行.

@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    private RabbitOperations rabbitOperations;
    @Test
    void contextLoads() {
		//仅发送,不需要等待返回值,异步,推荐
        rabbitOperations.convertAndSend("fs.dire"(交换机名称),"xy.q1"(路由key),"你好 rabbitmq"(消息内容));
        //发送,有返回值,使得rabbitmq变成同步的了,需要等待返回值,失去了rabbitmq的意义
        //rabbitOperations.convertSendAndReceive("","","");
    }
}

二.消费者
1.pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.yml

spring:
  rabbitmq:
    host: localhost
    port: 32769
    username: guest
    password: guest

3.监听队列
修饰符: public
返回值: 异步消息必须是void
方法名: 自定义
参数表: 一个参数,类型可以是Message或者具体的消息体类型(Object)。message是Spring AMQP中消息的唯一类型。代表完整消息,有头和体组成。如果对消息头没有任何处理要求,则直接定义消息体具体类型即可。
方法实现: 根据具体要求,定义即可。

注意:方法可以抛出任意类型的异常。只要抛出异常,则代表消费错误,RabbitMQ不删除队列中的消息。
注解是RabbitListener。

如果有多个消费监听,默认采用轮询消费,消费监听不需要相互等待,并发执行.

@Component
public class StringMessageConsumer {
    @RabbitListener(queues = {"q1"})(队列名称可以指定多个)
    public void received1(String m){
        System.out.println(m);
    }

	@RabbitListener(queues = {"q1"})(队列名称可以指定多个)
    public void received2(String m){
        System.out.println(m);
    }
}

2.Fanout交换器

扇形交换器: 会把消息发送给所有的绑定在当前交换器上的队列,可以不写路由键
1.在Queues界面创建队列f1,f2,在Exchanges创建fanout交换机fs.fanout绑定交换机f1,f2,不指定路由
2.编写测试代码

    @Autowired
    private RabbitOperations rabbitOperations;
    @Test
    void contextLoad1s() {
    	//没有绑定路由键,设为null即可
        rabbitOperations.convertAndSend("fs.fanout",null,"你好 rabbitmq");
    }

3.f1,f2队列都多了一个消息

3.Topic交换器

主题交换器: 路由键可包括特殊字符实现通配。特殊字符包括: 星号和 ‘#’。
星号 : 代表一个单词。多个单词使用’.'分割。
‘#’ : 代表0~n个字符,即任意字符串。//如abc.# 代表以 abc. 开头的所有路由key
1.在Queues界面创建队列t1,t2,t3在Exchanges创建Topic交换机fs.topic绑定交换机t1,t2,t3
2.指定t1路由键: abc.t1,指定t2路由键: abc.*,指定t3路由键: abc.#
3.编写测试代码

    @Autowired
    private RabbitOperations rabbitOperations;

    @Test
    void contextLoawd1s() {
        rabbitOperations.convertAndSend("fs.topic","abc.t1","你好 rabbitmq");//t1,t2,t3
        rabbitOperations.convertAndSend("fs.topic","abc.123","你好 rabbitmq");//t2,t3
        rabbitOperations.convertAndSend("fs.topic","abc.123.234","你好 rabbitmq");//t3
    }

4.headers交换器

headers交换器和direct交换器的主要区别是在传递消息时可以传递header部分消息
生产消息
在Queues界面创建队列h1,在Exchanges创建headers交换机fs.headers绑定交换机h1,指定路由key,fs.h1

    @Autowired
    private RabbitOperations rabbitOperations;
    
    @Test
    void conwtextLoawd1s() {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("fs","java");
        Message message = new Message("我是消息".getBytes(), messageProperties);
        rabbitOperations.convertAndSend("fs.headers","fs.h1",message);
    }

消费消息

@RabbitListener(queues = "header")
public void headerMsg(Message msg) {
    System.out.println(new String(msg.getBody()));
    Map<String, Object> map = msg.getMessageProperties().getHeaders();
    // 如果没有指定key返回null
    System.out.println(map.get("cou"));
}

对象类型消息传递

1.生产者
创建User类
注意要继承序列化接口Serializable
同时指定序列化版本号serialVersionUID且和消费者一致(如果不指定,消费者的User类必须和生产者的User类完全一致,否则,不会认为是统一类型)
User类在生产者和消费者中的包路径必须一致

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private final Long serialVersionUID = 1L;
    private String name;
    private Integer age;
}

发送消息
    	@Autowired
    	private RabbitOperations rabbitOperations;
    
        User user = new User("张三",18);
        rabbitOperations.convertAndSend("fs.direct","xy.q1",user);

2.消费者
在消费者中创建User类
注意要继承序列化接口Serializable
同时指定序列化版本号serialVersionUID且和消费者一致(如果不指定,消费者的User类必须和生产者的User类完全一致,否则,不会认为是统一类型)
User类在生产者和消费者中的包路径必须一致
直接用对应类型接收

@Component
public class StringMessageConsumer {
    @RabbitListener(queues = {"q1"})
    public void received(User(类型和发送来的一致即可) user){
        System.out.println(user);
    }
}

统一用Message对象接收,常用语Headers交换器

@Component
public class StringMessageConsumer {
    @RabbitListener(queues = {"q1"})
    public void received(Message message) throws IOException, ClassNotFoundException {
        byte[] body = message.getBody();
        
        ByteArrayInputStream bai = new ByteArrayInputStream(body);
        ObjectInputStream ois = new ObjectInputStream(bai);
        
        Object object = ois.readObject();
        if(object instanceof User){
            User user = (User) object;
            System.out.println(user);
        }
    }
}

同步等待

等待结果返回,才能往下执行
默认等待5s,可以配置,超时返回null
yml配置

spring:
  rabbitmq:
    host: localhost # RabbitMQ服务器的IP。默认localhost
    port: 32769 # RabbitMQ服务器的端口。
    username: guest # RabbitMQ的访问用户名。默认guest。
    password: guest # RabbitMQ的访问密码。默认guest
    virtual-host: / # 连接RabbitMQ中的哪一个虚拟主机。默认 /

生产者:

    @Test
    void contextLowads() {
    	//发送,有返回值,使得rabbitmq变成同步的了,需要等待返回值,等待结果返回,才能往下执行
        String aa = (String)rabbitOperations.convertSendAndReceive("fs.direct", "xy.q1", "aa");
        System.out.println(aa);
}

消费者:

@Component
public class StringMessageConsumer {
    @RabbitListener(queues = {"q1"})
    public String received(Message message) {
        return "hello";
    }
}

使用代码创建队列

一.在生成者端创建队列
是发送消息时创建,而不是启动项目时。

@Configuration
public class RabbitMQConfig {
    // 发送消息时如果不存在这个队列,会自动创建这个队列。
    // 注意:是发送消息时,而不是启动项目时。
    // 相当于:可视化操作时创建一个队列
    // 如果队列创建完成后,没有绑定(没有另外两个方法),默认绑定到AMQP default交换器
    @Bean
    public Queue queue(){
        return new Queue("queue.second");
    }

    // 如果没有这个交换器,在发送消息创建这个交换器
    // 配置类中方法名就是这个类型的实例名。相当于<bean id="" class="">的id属性,返回值相当于class
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("direct.first.ex");
    }

    // 配置类中方法参数,会由Spring 容器自动注入
    @Bean
    public Binding directBingding(DirectExchange directExchange,Queue queue){
        // with(“自定义路由键名称”)
        return BindingBuilder.bind(queue).to(directExchange).with("routing.key.2");
        // withQueueName() 表示队列名就是路由键名称
        // return BindingBuilder.bind(queue).to(directExchange).withQueueName();
    }
}

二.消费者

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(name = "queue.second",autoDelete = "false", durable = "true"),
                    exchange = @Exchange(name = "direct.second.ex",autoDelete = "false", type = ExchangeTypes.DIRECT),
                    key = {"routing.key.second.1"}
            )
    })
    public void onMessage(String messageBody){
        System.out.println("第二个消息消费者监听,处理消息:" + messageBody);
    }

开启生产者确认消息发送到交换机和队列

当Publisher发送消息时,需要先连接到Broker(RabbitMQ实例),再进入到virtual-host(虚拟主机),最后到达Exchange(交换器)。如果无法到达交换器,RabbitMQ会触发RabbitTemplate中的回调机制,通知Publisher消息发送的失败原因。
1.yml配置

spring:
  rabbitmq:
    host: 192.168.91.128 # RabbitMQ服务器的IP。默认localhost
    port: 5672 # RabbitMQ服务器的端口。
    username: bjsxt # RabbitMQ的访问用户名。默认guest。
    password: bjsxt # RabbitMQ的访问密码。默认guest
    virtual-host: / # 连接RabbitMQ中的哪一个虚拟主机。默认 /
    确认消息是否发送到交换机,默认不开启
    publisher-confirm-type: correlated # 开启到达交换器确认机制。默认值:none,不开启确认机制。
    确认消息是否发送到队列,因为队列和交换机绑定唯一路由,默认不开启
	publisher-returns: true

生产者添加配置类

@Component
public class RabbitMQConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * @PostConstruct 是 Spring Framework 中的一个注解,通常用于标记一个方法,
     * 在对象被创建后(执行构造方法后)立即执行该方法。
     */
    @PostConstruct
    public void init(){
        //设置RabbitTemplate中的回调逻辑
        this.rabbitTemplate.setConfirmCallback(this);
        this.rabbitTemplate.setReturnCallback(this);
    }
		
    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
//无论消息是否到达交换机该方法都会执行
        System.out.println("消息唯一标记 : " + correlationData);
        System.out.println("是否确认到达交换器 : " + b);
        System.out.println("不能到达交换器的原因 : " + s);
    }

/**
交换机消息成功到队列不执行该方法,否则执行,消息没有到达交换机,也不执行
*/
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("交换器 : " + exchange);
        System.out.println("路由键 : " + routingKey);
        System.out.println("路由失败编码 : " + replyCode);
        System.out.println("路由失败描述 : " + replyText);
        System.out.println("消息 : " + message);
    }
}

生产消息:

rabbitOperations.convertAndSend("fs.direct","fs.fs_direct","hello world");

成功发送到交换机但交换机没有发送到消息队列:
消息唯一标记 : null
是否确认到达交换器 : true
不能到达交换器的原因 : null
交换器 : fs.direct
路由键 : fs.fs_direct111
路由失败编码 : 312
路由失败描述 : NO_ROUTE
消息 : (Body:'hello world' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
--------------------------------------------------------
没有成功发送到交换机: 
消息唯一标记 : null
是否确认到达交换器 : false
不能到达交换器的原因 : channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'fs.direct1' in vhos

消费者:

    @RabbitListener(queues = {"fs_direct"})
    public void cu(String str){
        System.out.println(str);
    }

开启生产者消息消费确认

在Spring AMQP中,消费者(Consumer)默认的ACK机制是自动确认,即消费代码正常执行结束,立刻确认消息已消费;消费代码发送异常,相当于消息未消费。如果希望关闭自动ACK机制,可使用两种处理方案实现。
1.重试消费
​ 可以在消费者中基于配置开启重试机制,并设置重试消费次数(默认一直重试)。当消费消息发生错误,导致未确认(NACK)时,消费者尝试重复消费消息;当重复消费次数到达设置阈值后,强制确认(ACK),RabbitMQ会移除队列中的消息。

spring:
  rabbitmq:
    host: 192.168.91.128
    port: 5672
    username: bjsxt
    password: bjsxt
    listener:
      simple:
        retry:
          enabled: true # 开启重试机制
          max-attempts: 2  # 重试消费2次

消费者:

@Component
public class StringMessageConsumer {
    @RabbitListener(queues = {"queue.first"})
    public void onMessage(String messageBody){
        System.out.println("第一个消息消费者监听,处理消息:" + messageBody);
        int i = 1/0; // 模拟消费失败
    }
}

此时重试两次后会移除队列中的消息。

2.手工确认ACK
yml配置

spring:
  rabbitmq:
    host: 192.168.91.128
    port: 5672
    username: bjsxt
    password: bjsxt
    listener:
      simple:
        acknowledge-mode: manual # 手工确认。 默认AUTO,自动确认
  /**
     * 消费方法。实现手工ACK确认
     * @param messageBody 消息内容
     * @param channel 信道对象,当一个消费者向 RabbitMQ 注册后,会建立起一个 Channel ,
     *                RabbitMQ 会用 basic.deliver 方法向消费者推送消息。
     * @param deliveryTag  RabbitMQ向该Channel投递的这条消息的唯一标识 ID,
     *                     是一个递增的正整数,delivery tag 的范围仅限于 Channel
     */
    @RabbitListener(queues = {"fs_direct"})
    @GetMapping("/cu")
    public void cu(String str, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag){
        try {
            System.out.println(str);
            int a = 1/0;
            /**
             * 参数1 - 消息的唯一标识
             * 参数2 - 是否批量提交。手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 deliveryTag 小于等于传入值的所有消息
             */
            channel.basicAck(deliveryTag,true);

        } catch (Exception e) {

            try {
                if(deliveryTag < 3){
                    /*
                     * 参数1 - 消息的唯一标识
                     * 参数2 - 是否批量提交
                     * 参数3 - 是否重新发出消息。
                     */
                    channel.basicNack(deliveryTag, false, true);
                    System.out.println("重新发送消息次数:" + deliveryTag);
                }else {
                    /**
                     *参数1 - 消息的唯一标识
                     *参数2 - 是否重新发出消息。false则废弃此消息。
                     */
                    channel.basicReject(deliveryTag,false);
                    System.out.println("强制ACK确认");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

创建死信队列和死信交换机并使用

当消息变成死信后,它会被重新发送到另一个交换器(DLX:死信交换机),然后根据路由规则投递到另一个队列(DLQ:死信队列)。
消息变成放入死信队列的几种情况:

  1. 消息被拒绝(basicNack 或 basicReject)时,且未重新排队(requeue = false)。参考ACK确认机制。
  2. 消息过期。即消息头中的TTL过期。
  3. 队列达到最大长度时。

1.DLX就是一个普通的交换器为,最好是Topic ->sx.channel
2.创建DLQ(死信队列)并绑定到DLX(死信交换器)上,DLQ就是一个普通的队列 -> sx.queue

3.创建任意交换器,:必须设置队列参数 x-dead-letter-exchange,此参数用于绑定死信处理逻辑,即信息成为死信后,投递到哪个死信交换器。-> dd.queue
4.创建任意队列并绑定到交换器 -> dd.channel
5…发送消息:

    public void show(){
        String messageBody = "投递到test.queue队列中的消息,等待超时后投递到DLX中再处理";
        MessageProperties messageProperties =
                new MessageProperties();
        // 设置消息持久化
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        // 设置消息体字符集
        messageProperties.setContentEncoding("UTF-8");
        // 设置消息超时时间,单位毫秒,参数类型是字符串
        messageProperties.setExpiration("7000");
        // 基于字符串消息体内容和消息参数,创建要传递的消息对象。
        Message message = new Message(messageBody.getBytes(), messageProperties);
        rabbitOperations.convertAndSend("abc","dd",message);
    }

5.观察到rabbitmq队列中dd.queue中有一条消息,过7秒后,该消息转移到了sx.queue中.

面试题

RabbitMQ为什么需要信道?为什么不是TCP直接通信?

1.TCP的创建和销毁开销大,需要3次握手和4次挥手。
2.没有信道,每个应用程序都需要单独建立TCP连接,会导致资源浪费和性能瓶颈。
3.信道允许多线程共享单个TCP连接,提高效率,避免性能问题。

RabbitMQ如何保证消息的可靠性和消息正常的传递?
发送者:
1、发送者到交换器之间的消息安全保证 Confirm模式 ConfirmCallback
2、交换器和队列之间消息的安全保证 Returns模式 ReturnsCallback
消费者:
1.默认:自动开启ACK机制 消费者(Consumer)默认的ACK机制是自动确认,即消费代码正常执行结束,立刻确认消息已消费
2.重试策略: 我们可以通过yml中配置的方式 指定重试次数 到达重试次数后强制的进行ACK确认 这个时候队列中消息就没了
3.手动配置ACK机制:
channel.basicAck(deliveryTag, false); //ACK确认
channel.basicNack(deliveryTag, false, true); //NACK
channel.basicReject(deliveryTag, false);//拒绝: 强制的ACK确认
RabbitMQ的服务:队列 和交换器 都实现了持久化方案,自动把MQ中信息持久化到磁盘中

如何保证消息幂等性/如何保证消息不被重复消费?

保证消息不重复提交
mp.setMessageId(UUID.randomUUID().toString());
保证消息不重复消费
使用Redis保存每一次的消息头中id 每一次调用之前判断redis中是否存在指定的数据
如果redis中对应的id不存在的,证明没有被消费 直接消费即可
如果redis中对应的id 存在的 证明当前消息已经被消费了 ,直接返回 /提示 /拒绝即可

如何保证消息的顺序性?【面试题】

方案A:拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦
方案B:就一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理。

有几百万消息持续积压几小时怎么解决?【面试题】

一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:

  1. 先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉。
  2. 新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。
  3. 然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
  4. 接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。
  5. 这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息。

消息队列过期失效问题?

这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,
就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期
以后,这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入mq里面去,把白天丢的数据
给他补回来。也只能是这样了。假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个
订单给查出来,手动发到mq里去再补一次.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值