RabbitMQ高级应用

死信队列

死信与毒消息不同之处在于,死信是长时间没消费的消息,而毒消息则是经过多次消费而消费失败的消息。
死信队列和普通队列一样,可以消费队列中的消息。

产生死信队列的场景

  1. 在TTL实现内未消费的消息会进入死信队列。
  2. 队列长度已经满了,无法装载进队列中的消息会进入死信队列。
  3. 消息被消费者拒绝。

配置死信交换机和死信队列

死信交换机和死信交换机与普通队列和普通交换机一样,创建之后设置队列的路由key即可
image

创建需要绑定死信队列的队列

一般需要以下几个参数
死信交换机:x-dead-letter-exchange
死信路由键,这个路由键配置的与死信交换机中的队列的路由键一致即可:x-dead-letter-routing-key
x-message-ttl: 过期时间
durable:持久化

这是我配置的死信队列,当队列中的消息有上面介绍的几种情况之后,会根据配置死信交换机和路由键匹配队列。
image

之后模拟这样一个场景,当消息进行消费时,调用channel.basicReject或者channel.basicNack对消息进行拒绝。

    @RabbitListener(queues = {"dead-queue"})
    public void fanoutConsumer(Message message, Channel channel, String messageStr) {
        log.info("message:{}", message);
        try {
            log.info("拒绝策略");
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

在这之前还需要将自动提交设置为手动提交

spring:
  rabbitmq:
    addresses: 192.168.71.134
    port: 5672
    username: lyra
    password: lyra
    listener:
      simple:
        prefetch: 1
        concurrency: 5
        max-concurrency: 10
        acknowledge-mode: MANUAL

当有消息进入时,因为使用了拒绝策略,可以看到已经自动路由到死信队列中了
image

在web端中也可以配置死信队列的参数
image

死信队列的消费与普通队列的消费一样
死信队列可以用于做延迟队列 当队列中的消息在一定时间未消费便会进入死信队列
延迟队列用于订单场景等等。

消费优先级

通过配置消费优先级来使配置好的服务器消费的消息多一些,配置差的服务器消费的数据少一些。
通过设置消费者来指定消费者的优先级,设置的数字越大,消费者消费队列时优先级越高。
RabbitMQ默认采用的是round-robin模式,该模式是以平均的方式将消息推送到每个消费者中的。

还可以使用channel.basicQos(int
prefetch_size, int prefetch_count, boolean global)
该方法的第一个参数是用于标识每次拉取消息的最大未答复消息数,一旦拉的消息等于这个未答复消息数,那么将不会再进行拉取消费消息。

数据分发

将数据分发到别的节点中,便于访问区域较近的节点进行访问。
比如在大型企业中,山西和长沙分别部署了Rabbit集群,消费消息时需要从山西的集群中获取消息进行消费,这样并访问速度会变慢,可以使用数据分发来解决这个问题,将山西的节点设置为上有节点,下游获取消息时会从上游拉取消息进行同步,使用时直接消费下游本地服务器即可。

安装插件

插件需要在上下游服务器都需要配置

rabbitmq-plugins enable rabbitmq_federation
rabbitmq-plugins enable rabbitmq_federation_management

安装之后既可以在admin面板中看到相应的配置
image

配置

image
如图所示我们需要在下游服务器配置交换机用于接收上游服务器的数据
创建交换机并绑定队列
image

首先进入Federation Upstreams界面,下游服务器配置上游节点
image
name:上游节点名称
url:amqp协议地址,需要注意的是如果使用的是默认的/ 虚拟主机,那么ip后面什么都不需要填,否则将无法进行启动。
上面两项是必须项,必须填 剩余项则是选填 剩余项则可以选择?来查看含义。
配置之后如下所示
image
之后需要在Policies界面配置交换机与上游配置的映射关系
name为策略名称
pattern为配置的交换机名称,可以是正则表达式匹配多个交换机
下面两项则配置的是上游名称,如果是all的话,那么就是所有的上游进行匹配。
image
配置完毕之后进入Federation Status界面可以看到有节点已经running了
表示已经配置成功
image
之后去上游交换机界面我们可以看到在策略界面中选择的自动创建的交换机以及灰色的映射关系的交换机节点
image
上游发消息是,可以向交换机中发消息,也就是lyra-exchange交换机,发送的消息会自动转发到下游中。

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.71.136");
        connectionFactory.setUsername("lyra");
        connectionFactory.setPassword("lyra");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare("lyra-exchange", "fanout", true, false, null);
        channel.basicPublish("lyra-exchange", "", null, "lyra heartstrings".getBytes(StandardCharsets.UTF_8));

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

在下游的交换机队列中确实可以获取消息
image

懒队列

普通队列是将消息存储到内存中的,而懒队列则优先将消息存储到硬盘中,使用消息时先从硬盘中读取消息,这样做是优化了内存容量小存储消息少的问题或服务器宕机消息丢失额问题。
在控制台中指定这样一个参数便可以创建懒队列
image

消息分片

通过消息分片的方式将消息存储到多个队列中,这样做可以使吞吐量变高,原因可以类比售票系统,一个售票台肯定没有多个售票台吞吐量高。

安装sharding插件

 rabbitmq-plugins enable rabbitmq_sharding

配置

首先新建一个策略用于匹配以sharding开头的交换机
需要在参数中指定分片数和路由key 路由key必须是sharding
image
之后创建一个交换机,类型为x-modulus-hash
image

插件将会在交换机中创建分片数目的队列
image

发送消息时直接指定交换机发消息即可,需要注意的是每个队列并不能以均匀的方式存储消息,所以他们不是有序队列,消费者数量必须大于分片数量,否则有些队列中的数据消费不到。需要注意的是,发送消息是路由key必须是随机的,否则会到同一个队列中。

public class Main {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.71.136");
        connectionFactory.setUsername("lyra");
        connectionFactory.setPassword("lyra");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare("sharding-test", "x-modulus-hash", true, false, null);

        for (int i = 0; i < 10000; i++) {
            channel.basicPublish("sharding-test", UUID.randomUUID().toString(), null, "lyra heartstrings".getBytes(StandardCharsets.UTF_8));

        }

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

image

消费时需要创建一个虚拟队列,队列名称和sharding交换机的名称一直,然后创建多个消费者,消费者将会以轮询的方式从分片中获取数据

        channel.queueDeclare("sharding-test", true, false, false, null);

        for (int i = 0; i < 3; i++) {
            channel.basicConsume("sharding-test", new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println(new String(body));
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            });
        }
posted @   RainbowMagic  阅读(87)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示