RabbitMQ 高级特性 (三)

死信队列

  • 死信队列是RabbitMq中非常重要的一个特性,简单来说,他是对RabbitMQ未能正常消费的一种补偿机制。死信队列也是一个普通的队列,同样可以在队列上声明消费者,继续对消息进行消费处理。
  • 主要配置参数
    // 对应的死信交换机
    x-dead-letter-exchange: mirror.dlExchange 
    // 对应的路由Key
    x-dead-letter-routing-key
    //消息过期时间
    x-message-ttl
    
  • 何时会产生死信队列
    • 消息被消费者确认拒绝,消费者把requeue参数设置为true(false),并且在消费后,向RabbitMQ返回拒绝。channel.basicReject或者channel.basicNack。
    • 消息达到设置的TTL过期时间
    • 消息队列达到最长被丢弃的消息

设置TTL过期的两种方式:

  • 可以在申明队列的时候设置时间,也可以在发送消息的时候设置
  • 通过配置策略制定
    • rabbitmqctl set_policy TTL ".*" '{"message-ttl":60000}' --apply- to queues
  • 代码配置,在业务不是很大的情况下,我觉得在代码配置更利于阅读开发
@Bean
Queue declareTTLQueue() {
    return new Queue("test.ttl.001", true, false, false, Map.of("x-message-ttl", 6000));
}

配置死信队列:实现延迟队列或补偿机制:

使用说明:声明一个死信队列(和普通队列没啥区别),在声明一个正常消费队列,此时通过可配置参数将死掉的消息转发到那个交换机上同时设置上RoutingKey值,再监听死信队列配置消费者,进行业务处理

 		@Bean
        Queue declareTTLQueue() {
            // Queue build = QueueBuilder.durable("test.ttl.001").ttl(6000).deadLetterExchange(TEST_EXCHANGE).deadLetterRoutingKey(TEST_DEAD_LETTER_ROUTE).build();

            return new Queue("test.ttl.001", true, false, false, Map.of("x-message-ttl", 6000,
                    "x-dead-letter-exchange", TEST_EXCHANGE, "x-dead-letter-routing-key", TEST_DEAD_LETTER_ROUTE));
        }


        /**
         * 配置死信队列
         */

        @Bean
        public void declareDeadLetterQueue() {
            Exchange testExchange = ExchangeBuilder.directExchange(TEST_EXCHANGE).build();
            amqpAdmin.declareExchange(testExchange);

            Queue testQueue = QueueBuilder.durable(TEST_DEAD_LETTER_QUEUE).build();
            amqpAdmin.declareQueue(testQueue);

            amqpAdmin.declareBinding(BindingBuilder.bind(testQueue).to(testExchange).with(TEST_DEAD_LETTER_ROUTE).noargs());
        }

如何确定一个消息是否是死信消息:

  • 消息被作为死信转移到死信队列后,会在Header当中增加一些消息。在官网的详细介绍中,可以看到很多内容,比如时间、原因(rejected,expired,maxlen)、队列等。然后header中还会加上第一次成为死信的三个属性,并且这三个属性在以后的传递过程中都不会更改。
    • x-first-death-reason
    • x-first-death-queue
    • x-first-death-exchange
     Map<String, Object> headers = properties.getHeaders();
     headers.forEach((key, value) -> System.out.println("header key: " + key + "; value: " + value));
    

通过 rabbitmq_delayed_message_exchange插件,实现延迟队列

  • TTL+死信队列实现延迟队列是有局限性的,列如第一个值60秒过期,第二个值6秒,当第二个消息过期之后也不会出队。FIFO机制
  • GitHub 插件地址

安装

// 查找安装位置
whereis rabbitmq

// 将插件包移动到服务 plugins 目录下,我的服务地址   /usr/lib/rabbitmq/lib/rabbitmq_server-3.9.15
// 启用延时插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

注意点:如果是集群服务,以RAM 加入的节点无法启用延时插件,安装成功后,创建Exchange时就多出了 x-delayed-message类型
在这里插入图片描述

代码演示:

  • API 方式 -声明且发送
public class SendMessageDelay {


        public static void main(String[] args) throws IOException, TimeoutException {

            Connection connection = RabbitMqUtil.getConnection();
            Channel channel = connection.createChannel();

            Map<String, Object> param = new HashMap<>(1);
            param.put("x-delayed-type", "direct");  //声明交换机类型,并且延时

            channel.exchangeDeclare("delay.exchange", "x-delayed-message",true,false,false,param);

            channel.queueDeclare("delay.queue", true, false, false, null);
            channel.queueBind("delay.queue", "delay.exchange", "delay");


            Map<String, Object> headers = new HashMap<>(1);
            headers.put("x-delay", 10000);   //设置消息过期时间

            AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
            builder.headers(headers);

            channel.basicPublish("delay.exchange", "delay", builder.build(), "这是一个集成延时插件发送的消息".getBytes(StandardCharsets.UTF_8));

            channel.close();
            connection.close();


        }
    }

  • Spring boot 方式

	 /**
	 * 声明延时队列-插件版
	 */
	 @Bean
	 public void declareDelayExchange() {
	     Exchange delayExchange = ExchangeBuilder.directExchange("test.delay").delayed().build();
	     amqpAdmin.declareExchange(delayExchange);
	
	     Queue testQueue = QueueBuilder.durable(TEST_DEAD_LETTER_QUEUE).build();
	     amqpAdmin.declareQueue(testQueue);
	
	     amqpAdmin.declareBinding(BindingBuilder.bind(testQueue).to(delayExchange)
	            .with(TEST_DEAD_LETTER_ROUTE).noargs());
	        }

	 /**
	 * 发送延时消息
	 */
	@Slf4j
	@SpringBootTest
	class WangfamilyMqApplicationTests {
	
	    @Autowired
	    RabbitTemplate rabbitTemplate;
	    @Test
	    void contextLoads() {
	        rabbitTemplate.convertAndSend("test.delay", TEST_DEAD_LETTER_ROUTE, new Message("Spring boot , Hello Word".getBytes()), new MessagePostProcessor() {
	            @Override
	            public Message postProcessMessage(Message message) throws AmqpException {
	                message.getMessageProperties().setHeader("x-delay", 6000);
	                return message;
	            }
	        });
	    }
	}

	/**
	 * 消费消息
	 */
	@Component
    public class DelayMessageListener {


        @RabbitListener(queues = TEST_DEAD_LETTER_QUEUE)
        public void handle(Channel channel, String body, Message message) {

            System.out.println(body);

            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e) {
                try {
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
    }

消费优先级与流量控制

  • 消费者通过 x-priority 参数配置,来控制消息的优先消费权
  • 通过prefetch_count 参数,控制消费者未应答的消息数量,可以通过单消费者和1个未应答量来实现消息的顺序消费
  • 大神文章,细节满满,值得一看

懒队列 Lazy Queue

  • 懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。懒队列的设计目标是为了支持非常长的队列(数百万级别)。队列可能会因为一些原因变得非常长-也就是数据堆积
    • 消费者服务宕机了、生产者发送消息大于消费者消费消息速度,产生数据堆积问题,影响RabbitMq性能

声明懒队列的两种方式

  • 通过配置参数设置

    	Map<String, Object> args = new HashMap<String, Object>();
    	args.put("x-queue-mode", "lazy");
    	channel.queueDeclare("myqueue", false, false, false, args);
    
  • 通过设置RabbitMQ 策略

    rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"default"}' --apply-to queues
    

注意点:懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的

消息分片存储插件-Sharding Plugin

  • 通过消息分片生成伪队列消费,解决长队列问题,暂且了解,不做深入,自行探索
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值