RabbitMQ消息可靠性保证机制4--消费端限流

RabbitMQ:消息限流与延时队列 这种方式下消息一定要进行手动签收,因为之前的文章中我们讲过,自动签收是消息一达到消费端就进行签收了,可能我们的业务逻辑还没运行就已经进行签收了,所以自动签收状态下开启限流几乎没有作用。的基因中没有延时队列这回事,它不能直接指定一个队列类型为延时队列,然后去延时处理,但是经过上面两节的铺垫,我们可以将TTL+DLX相结合,这就能组成一个延时队列。支持消息的过期时间,在消息发送时可以进行指定,也支持队列的过期时间,从消息入队列开始计算,只要超过了队列的超时时间配置,那么消息会自动的清除。 阅读详情

7.7 消费端限流

在类似如秒杀活动中,一开始会有大量并发写请求到达服务端,城机对消息进行削峰处理,如何做?

当消息投递的速度远快于消费的速度时,随着时间积累就会出现“消息积压”。消息中间件本身是具备一定的缓冲能力的,但这个能力是有容量限制的,如果长期运行并没有任何处理,最终会导致Broker崩溃,而分布式系统的故障往往会发生上下游传递,连锁反应可能会引起系统大范围的宕机,这就很悲剧了

7.7.1 资源限制限流

在RabbitMQ中可对内存和磁盘使用量设置阈值,当达到阈值后,生产者将被阻塞(block),直到对应指标恢复正常。全局上可以防止超大流量、消息积压等导致的Broker被压垮。当内存受限或磁盘可用空间受限的时候,服务器都会暂时阻止连接,服务器将暂停从发布消息的已连接的客户端的套接字读取数据。连接心中监视也将被禁用。所有网络连接将在rabbitmqctl和管理插件中显示为“已阻止”,这意味着它们尚未尝试发布,因此可以或者被阻止,这意味着它们已发布,现在已暂停。兼容的客户端被阻止将收到通知。

在/etc/rabbitmq/rabbitmq.conf中配置磁盘可用空间大小:

# 磁盘限制阈值设置
# 设置磁盘的可用空间大小,单位字节。当磁盘可用空间低于这个值的时候,发生磁盘告警,触发限流。
# 如果设置了相对大小,则忽略此绝对大小。
disk_free_limit.absolute = 2000

# 使用计量单位,从RabbitMQ 3.6.0开始有效,对vm_memory_high_watemark同样有效。
# disk_free_limit.absolute = 500KB
# disk_free_limit.absolute = 50mb
# disk_free_limit.absolute = 5GB

# Alternatively, we can set a limit relative to total avaliable RAM.
# Values lower than 1.0 can be dangerous and should be used carefully.
# 还可以使用相对于总可用内存来设置。注意,此值不要低于1.0!
# 当磁盘可用空间低于总可用内存的2.0倍的时候,触发限流
# disk_free_limit.relative = 2.0

# 内存限流阈值设置
# 0.4表示阈值总可用内存的比值。 总可用内存表示操作系统给每个进程分配的大小,或者实际内存大小。
# 如32位的Windows,系统给每个进程最大2GB的内存,则此比值表示阈值为820MB
vm_memory_high_watermark.relative = 0.4

# 还可以直接通过绝对值限制可用内存大小,单位字节
vm_memory_high_watermark.absolute = 1073741824

# 从RabbitMQ 3.6.0开始,绝对值支持计量单位。如果设置了相对值,则忽略此设置值
vm_memory_high_watermark.absolute = 1024MiB



k, kiB : kibibytes(2^10 - 1024 bytes)
M, MiB : mibibytes(2^20 - 1024576 bytes)
G, GiB : gibibytes(2^30 - 1073741824 bytes)

KB: kilobytes (10^3 - 1000 bytes)
MB: megabytes (10^6 - 1000000 bytes)
GB: gigabytes (10^9 - 1000000000 bytes)

可以通过两种来设置生效

  1. 临时生效

    此配制仅当前生效在重启后将失效。

# 硬盘资源限制
rabbitmqctl set_disk_free_limit 68996808704
# 内存资源限制
rabbitmqctl set_vm_memory_high_watermark 0.4

样例:

[root@nullnull-os rabbitmq]# rabbitmqctl set_disk_free_limit 68996808704
Setting disk free limit on rabbit@nullnull-os to 68996808704 bytes ...
  1. 长期生效

在rabbitmq.conf的配制文件中加入

# 硬盘限制 
disk_free_limit.absolute=68455178240

# 内存限制
vm_memory_high_watermark.relative = 0.4

样例:

[root@nullnull-os rabbitmq]# vi  /etc/rabbitmq/rabbitmq.conf 
# 加入以下内容,注意单位到字节
disk_free_limit.absolute=68455178240

[root@nullnull-os rabbitmq]# cat /etc/rabbitmq/rabbitmq.conf 
disk_free_limit.absolute=68455178240

[root@nullnull-os rabbitmq]# systemctl restart rabbitmq-server
[root@nullnull-os rabbitmq]# 

注意,此需要重启rabbitMQ才能生效。

磁盘限制配制参考

Configuring Disk Free Space Limit

The disk free space limit is configured with the disk_free_limit setting. By default 50MB is required to be free on the database partition (see the description of file locations for the default database location). This configuration file sets the disk free space limit to 1GB:

disk_free_limit.absolute = 1000000000

Or you can use memory units (KB, MB GB etc.) like this:

disk_free_limit.absolute = 1GB

It is also possible to set a free space limit relative to the RAM in the machine. This configuration file sets the disk free space limit to the same as the amount of RAM on the machine:

disk_free_limit.relative = 1.0

The limit can be changed while the broker is running using the rabbitmqctl set_disk_free_limit command or rabbitmqctl set_disk_free_limit mem_relative command. This command will take effect until next node restart.

The corresponding configuration setting should also be changed when the effects should survive a node restart.

来自:https://www.rabbitmq.com/disk-alarms.html

内存配制限制参考

https://www.rabbitmq.com/memory.html

Configuring the Memory Threshold

The memory threshold at which the flow control is triggered can be adjusted by editing the configuration file.

The example below sets the threshold to the default value of 0.4:

\# new style config format, recommended
vm_memory_high_watermark.relative = 0.4

The default value of 0.4 stands for 40% of available (detected) RAM or 40% of available virtual address space, whichever is smaller. E.g. on a 32-bit platform with 4 GiB of RAM installed, 40% of 4 GiB is 1.6 GiB, but 32-bit Windows normally limits processes to 2 GiB, so the threshold is actually to 40% of 2 GiB (which is 820 MiB).

Alternatively, the memory threshold can be adjusted by setting an absolute limit of RAM used by the node. The example below sets the threshold to 1073741824 bytes (1024 MiB):

vm_memory_high_watermark.absolute = 1073741824

Same example, but using memory units:

vm_memory_high_watermark.absolute = 1024MiB

If the absolute limit is larger than the installed RAM or available virtual address space, the threshold is set to whichever limit is smaller.

The memory limit is appended to the log file when the RabbitMQ node starts:

2019-06-10 23:17:05.976 [info] <0.308.0> Memory high watermark set to 1024 MiB (1073741824 bytes) of 8192 MiB (8589934592 bytes) total

The memory limit may also be queried using the rabbitmq-diagnostics memory_breakdown and rabbitmq-diagnostics status commands.

The threshold can be changed while the broker is running using the

rabbitmqctl set_vm_memory_high_watermark <fraction>

command or

rabbitmqctl set_vm_memory_high_watermark absolute <memory_limit>

For example:

rabbitmqctl set_vm_memory_high_watermark 0.6

and

rabbitmqctl set_vm_memory_high_watermark absolute "4G"

When using the absolute mode, it is possible to use one of the following memory units:

  • M, MiB for mebibytes (2^20 bytes)
  • MB for megabytes (10^6 bytes)
  • G, GiB for gibibytes (2^30 bytes)
  • GB for gigabytes (10^9 bytes)

中文配制可参考:https://www.cnblogs.com/kaishirenshi/p/12132703.html

更多配制可参见:https://www.rabbitmq.com/configure.html#config-file

样例程序:

maven导入

            <dependency>
                <groupId>com.rabbitmq</groupId>
                <artifactId>amqp-client</artifactId>
                <version>5.9.0</version>
            </dependency>

生产程序:

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeoutException;

public class ResourceLimitProduct {
  public static void main(String[] args) throws Exception {
    // 资源限制
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");

    try (Connection connection = factory.newConnection();
        Channel channel = connection.createChannel(); ) {

      // 定义交换器、队列和绑定
      channel.exchangeDeclare("res.limit.ex", BuiltinExchangeType.DIRECT, true, false, null);
      channel.queueDeclare("res.limit.qu", true, false, false, null);
      channel.queueBind("res.limit.qu", "res.limit.ex", "res.limit.rk");

      // 开启发送方确认机制
      AMQP.Confirm.SelectOk selectOk = channel.confirmSelect();

      ConfirmCallback confirm =
          new ConfirmCallback() {
            @Override
            public void handle(long deliveryTag, boolean multiple) throws IOException {
              if (multiple) {
                System.out.println("【批量确认】:小于" + deliveryTag + "已经确认");
              } else {
                System.out.println("【单条确认】:等于" + deliveryTag + "已经确认");
              }
            }
          };

      ConfirmCallback nackConfirm =
          new ConfirmCallback() {
            @Override
            public void handle(long deliveryTag, boolean multiple) throws IOException {
              if (multiple) {
                System.out.println("【批量不确认】:小于" + deliveryTag + "已经确认");
              } else {
                System.out.println("【单条不确认】:等于" + deliveryTag + "已经确认");
              }
            }
          };

      channel.addConfirmListener(confirm, nackConfirm);

      for (int i = 0; i < 100000000; i++) {
        String msg = getKbMessage(i);
        long sequence = channel.getNextPublishSeqNo();
        System.out.println("【发送】成功了序列消息:" + sequence);

        AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
        builder.contentType("text/plain");
        // 发送的消息持久化
        builder.deliveryMode(2);
        AMQP.BasicProperties properties = builder.build();

        channel.basicPublish(
            "res.limit.ex", "res.limit.rk", properties, msg.getBytes(StandardCharsets.UTF_8));

        Thread.sleep(ThreadLocalRandom.current().nextInt(5, 100));
      }
    } catch (IOException e) {
      e.printStackTrace();
    } catch (TimeoutException e) {
      e.printStackTrace();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  private static String getKbMessage(int i) {
    StringBuilder msg = new StringBuilder("发送确认消息:" + i + "--");
    for (int j = 0; j < 102400; j++) {
      msg.append(j);
    }
    return msg.toString();
  }
}

设置硬盘资源限制

[root@nullnull-os rabbitmq]# rabbitmqctl set_disk_free_limit 68996808704
Setting disk free limit on rabbit@nullnull-os to 68996808704 bytes ...

运行生产者的应用程序,查看控制台的输出

【发送】成功了序列消息:1
【单条确认】:等于1已经确认
【发送】成功了序列消息:2
【发送】成功了序列消息:3
【单条确认】:等于2已经确认
【发送】成功了序列消息:4
【单条确认】:等于3已经确认
【发送】成功了序列消息:5
......
【单条确认】:等于702已经确认
【单条确认】:等于703已经确认
【发送】成功了序列消息:704
【发送】成功了序列消息:705
【发送】成功了序列消息:706
【发送】成功了序列消息:707
【发送】成功了序列消息:708
【发送】成功了序列消息:709
【发送】成功了序列消息:710
【发送】成功了序列消息:711

到此使用硬盘空间限制的测试完成。

内存资源限制

编辑配制文件rabbitmq.conf

vi /etc/rabbitmqrabbitmq.conf 

# 添加配制
vm_memory_high_watermark.absolute=120M

重启让其生效

systemctl restart rabbitmq-server

检查配制生效情况

[root@nullnull-os rabbitmq]# rabbitmqctl environment

......
      {trace_vhosts,[]},
      {vhost_restart_strategy,continue},
      {vm_memory_calculation_strategy,rss},
      {vm_memory_high_watermark,{absolute,"120MB"}},
      {vm_memory_high_watermark_paging_ratio,0.5},
      {writer_gc_threshold,1000000000}]},
 {rabbit_common,[]},
......

查看到如下配制说明生效。

运行生产者

观察客户端输出

【发送】成功了序列消息:1
【发送】成功了序列消息:2
【单条确认】:等于1已经确认
【发送】成功了序列消息:3
【单条确认】:等于2已经确认
【单条确认】:等于3已经确认
【发送】成功了序列消息:4
【发送】成功了序列消息:5
【发送】成功了序列消息:6
【单条确认】:等于4已经确认
【单条确认】:等于5已经确认
【单条确认】:等于6已经确认
【发送】成功了序列消息:7
【单条确认】:等于7已经确认
......
【发送】成功了序列消息:174
【单条确认】:等于174已经确认
【发送】成功了序列消息:175
【单条确认】:等于175已经确认
【发送】成功了序列消息:176
【单条确认】:等于176已经确认
【发送】成功了序列消息:177
【发送】成功了序列消息:178
【发送】成功了序列消息:179
【发送】成功了序列消息:180
【发送】成功了序列消息:181
【发送】成功了序列消息:182
【发送】成功了序列消息:183
【发送】成功了序列消息:184
【发送】成功了序列消息:185
【发送】成功了序列消息:186
【发送】成功了序列消息:187

观察网页端的情况

在这里插入图片描述

到此内存资源限制而导致的限流测试完成。

7.7.2 默认的credit flow流控

RabbitMQ Credit Flow Mechanism (信用流控制机制) 是 RabbitMQ 使用的一种流量控制机制,旨在确保生产者(publishers)不会发送太多的消息给消费者(consumers),从而导致系统超载或资源耗尽。这个机制主要是为了保护消费者免受生产者发送太多消息的影响。

以下是 RabbitMQ Credit Flow 机制的基本工作原理:

  1. 信用计数器(Credit Counter):对于每个消费者,RabbitMQ 维护一个称为信用计数器的值。这个计数器表示消费者当前可以接收多少条消息。
  2. 初始信用额度(Initial Credit):当一个消费者连接到队列并开始消费消息时,RabbitMQ 为该消费者分配一个初始信用额度。这个额度通常与队列中的未确认消息数量有关。
  3. 消费者确认(Consumer Acknowledgments):当消费者成功处理一条消息并确认它时,它将会恢复一定数量的信用,这允许 RabbitMQ 将更多的消息发送给消费者。
  4. 信用降低(Decreasing Credit):当消费者未确认消息超出其信用额度时,其信用额度将降低。这会导致生产者无法继续发送消息给该消费者,直到其信用额度恢复。
  5. 自动降低的消费者(Auto-decrease Consumers):RabbitMQ 还可以配置为自动降低某些消费者的信用,以避免某个消费者占用太多资源。这通常用于处理慢速或长时间处理的消费者。

这个机制有助于平衡生产者和消费者之间的消息流量,防止生产者发送大量消息导致队列爆满,从而提高系统的稳定性和可靠性。

要注意的是,RabbitMQ 的信用流控制机制是可配置的,您可以根据您的需求来调整信用额度和其他参数,以满足特定的应用场景。此外,RabbitMQ 还提供了一些工具和插件,用于监控和管理流量控制,以确保系统的正常运行。

可以通过查看队列的状态信息来了解 Credit Flow 机制的当前状态。以下是一些常见的方式来查看 Credit Flow 状态:

  1. RabbitMQ Management UI:RabbitMQ 提供了一个基于 Web 的管理界面,您可以通过该界面查看队列的状态和统计信息,包括队列的消息数量、未确认消息数量以及消费者的状态。要访问管理界面,请确保已启用 RabbitMQ Management 插件。默认情况下,它通常在 http://localhost:15672/ 上运行。

    在管理界面中,您可以选择特定的队列,然后查看其状态和相关的统计信息,包括未确认消息数量。这可以帮助您了解 Credit Flow 是否生效,是否有消费者的信用已用尽。

  2. 命令行工具:您还可以使用 RabbitMQ 的命令行工具来查看队列的状态。以下是一个示例命令,用于查看队列的状态:

    rabbitmqctl list_queues name messages consumers messages_unacknowledged
    

    这将显示队列的名称、消息数量、消费者数量以及未确认消息数量。未确认消息数量表示消费者尚未确认的消息数量,这可以用于判断 Credit Flow 是否生效。

  3. 监控工具:您可以使用监控工具(如Prometheus和Grafana)来设置自定义监控和警报,以便实时跟踪队列的状态和信用流控制情况。通过这些工具,您可以创建仪表板来显示队列的各种指标,包括未确认消息数量和消费者的信用。

通过以上方法,您可以监视 RabbitMQ 中队列的状态和 Credit Flow 机制的工作情况,以确保系统的稳定性和可靠性。

在这里插入图片描述

7.7.3 Qos机制

RabbitMQ中有一种Qos保证机制,可以限制channel上接收到的未被Ack的消息数量,如果过这个数量限制RabbitMQ将不会再往消费端推送消息。是一种流控手段,可以防止大量消息瞬时从Broker送达消费端造成消费端巨大压力(甚至压垮消费端)需要注意的是Qos机制仅对消费端推模式有效,对拉模式无效。而且不支持NONE-ACK模式。

执行channel.basicConsume方法之前通过channel.basicQos方法可以设置该数量。消息的发送是异步的,消息的确认也是异步的。在消费慢的时候可以设置Qos的prefetchCount,它表示broker在向消费者发送消息的时候,一旦发送了prefetchCount个消息而没有一个消息确认的时候,就停止发送。消费者确认一个.broker就发送一个,确认两个就发送两个,换句话说,消费者确认多少,broker就发送多少,消费者等待处理的个数永远限制在prefetchCount个。

如果对于每个消息都发送确认,增加了网络流量,此时可以批量确认消息。如果设置了multiple为true,消费者在确认的时候,比如说id是8的消息确认了,则在8之前的所有消息都确认了。

生产者往往是希望自己产生的消息能快速投递出去,而当消息投递太快县城超过了下游的消费速度时就容易出现消息积压、堆积,所以,从上游来讲我们应该在生产端应用程序中也可以加入限流、应急开关等手段,避免超过broker端的极限承载能力或者压垮下游消费者。

再讲消费者,我们期望消费者能够尽快的消费完消息,而且还要防止瞬时大量消息压垮消费端(推模式),我们期望消费端能够处理速度是最快、最稳定而且还相对均匀(比较理想化)

提供应用吞吐量和缩短消费过程的耗时,主要以下几种方式:

  1. 优化应用程序的性能,缩短响应时间
  2. 增加消费节点实例。
  3. 调整并发消费的线程数。

测试

maven导入:

            <dependency>
                <groupId>com.rabbitmq</groupId>
                <artifactId>amqp-client</artifactId>
                <version>5.9.0</version>
            </dependency>

生产者

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;

public class QosProduct {
  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");

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

    // 定义交换机
    channel.exchangeDeclare(
        "qos.ex",
        BuiltinExchangeType.DIRECT,
        // 持久化标识
        false,
        // 是否自动删除
        false,
        // 属性信息
        null);

    for (int i = 0; i < 100; i++) {
      String msg = "这是发送的消息:" + i;
      channel.basicPublish("qos.ex", "qos.rk", null, msg.getBytes(StandardCharsets.UTF_8));
    }
  }
}

消费者 :

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.ThreadLocalRandom;


public class QosConsumer {
  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");
      
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    // 定义交换器、队列和绑定
    channel.exchangeDeclare("qos.ex", BuiltinExchangeType.DIRECT, false, false, null);
    channel.queueDeclare("qos.qu", false, false, false, null);
    channel.queueBind("qos.qu", "qos.ex", "qos.rk");

    // 设置Qos为5,未被确认ACK的为5,还有一个参数,即是否为全局,true为全局
    channel.basicQos(5);

    channel.basicConsume(
        "qos.qu",
        false,
        new DefaultConsumer(channel) {
          @Override
          public void handleDelivery(
              String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
              throws IOException {

            LocalDateTime time = LocalDateTime.now();
            System.out.println(
                "[消费]" + time + "+收到的消息:" + new String(body, StandardCharsets.UTF_8));

            int randomSleep = ThreadLocalRandom.current().nextInt(20, 1000);
            try {
              Thread.sleep(randomSleep);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }

            if (envelope.getDeliveryTag() % 3 == 0) {
              // 进行消息确认
              channel.basicAck(envelope.getDeliveryTag(), true);
            }
          }
        });
  }
}

测试

先启动消费都,再启动生产者,查看控制台输出

[消费]2023-08-25T12:08:13.143+收到的消息:这是发送的消息:0
[消费]2023-08-25T12:08:13.765+收到的消息:这是发送的消息:1
[消费]2023-08-25T12:08:14.127+收到的消息:这是发送的消息:2
[消费]2023-08-25T12:08:14.892+收到的消息:这是发送的消息:3
......
[消费]2023-08-25T12:08:57.437+收到的消息:这是发送的消息:96
[消费]2023-08-25T12:08:57.530+收到的消息:这是发送的消息:97
[消费]2023-08-25T12:08:57.566+收到的消息:这是发送的消息:98
[消费]2023-08-25T12:08:57.649+收到的消息:这是发送的消息:99

查看队列的情况:

[root@nullnull-os ~]# rabbitmqctl list_channels name,prefetch_count,global_prefetch_count --formatter pretty_table
Listing channels ...
┌───────────────────────────────────────────┬────────────────┬───────────────────────┐
│ name                                      │ prefetch_count │ global_prefetch_count │
├───────────────────────────────────────────┼────────────────┼───────────────────────┤
│ 61.170.208.88:59116 -> 10.0.4.16:5672 (1) │ 5              │ 0                     │
└───────────────────────────────────────────┴────────────────┴───────────────────────┘
[root@nullnull-os ~]# 

网页端查看

在这里插入图片描述

并行消费者

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;

public class QosThreadConsumer {
  public static void main(String[] args) throws Exception {

    // 资源限制
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");

    // 设置channel并发请求最大数
    factory.setRequestedChannelMax(5);

    // 自定义线程池工厂
    ThreadFactory thsFactory = Executors.privilegedThreadFactory();
    factory.setThreadFactory(thsFactory);

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

    // 定义交换器、队列和绑定
    channel.exchangeDeclare("qos.ex", BuiltinExchangeType.DIRECT, false, false, null);
    channel.queueDeclare("qos.qu", false, false, false, null);
    channel.queueBind("qos.qu", "qos.ex", "qos.rk");

    // 设置每秒处理2个
    channel.basicQos(5, true);

    channel.basicConsume(
        "qos.qu",
        false,
        new DefaultConsumer(channel) {
          @Override
          public void handleDelivery(
              String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
              throws IOException {

            LocalDateTime time = LocalDateTime.now();
            long threadId = Thread.currentThread().getId();
            System.out.println(
                "[消费]"
                    + time
                    + ",线程:"
                    + threadId
                    + ",收到的消息:"
                    + new String(body, StandardCharsets.UTF_8));

            int randomSleep = ThreadLocalRandom.current().nextInt(20, 1000);
            try {
              Thread.sleep(randomSleep);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }

            if (envelope.getDeliveryTag() % 3 == 0) {
              // 进行消息确认
              channel.basicAck(envelope.getDeliveryTag(), true);
            }
          }
        });
  }
}

控制台输出:

[消费]2023-08-26T09:37:21.430,线程:24,收到的消息:这是发送的消息:0
[消费]2023-08-26T09:37:21.866,线程:25,收到的消息:这是发送的消息:1
[消费]2023-08-26T09:37:22.434,线程:25,收到的消息:这是发送的消息:2
[消费]2023-08-26T09:37:22.847,线程:25,收到的消息:这是发送的消息:3
[消费]2023-08-26T09:37:23.685,线程:25,收到的消息:这是发送的消息:4
[消费]2023-08-26T09:37:23.847,线程:26,收到的消息:这是发送的消息:5
......
[消费]2023-08-26T09:39:10.684,线程:28,收到的消息:这是发送的消息:526
[消费]2023-08-26T09:39:10.695,线程:32,收到的消息:这是发送的消息:527
[消费]2023-08-26T09:39:10.767,线程:32,收到的消息:这是发送的消息:528
......
[消费]2023-08-26T09:39:58.270,线程:27,收到的消息:这是发送的消息:996
[消费]2023-08-26T09:39:58.405,线程:27,收到的消息:这是发送的消息:997
[消费]2023-08-26T09:39:58.575,线程:27,收到的消息:这是发送的消息:998
[消费]2023-08-26T09:39:58.671,线程:27,收到的消息:这是发送的消息:999

如果Qos设置为全局,则可以看到到

[root@nullnull-os ~]# rabbitmqctl list_channels name,prefetch_count,global_prefetch_count --formatter pretty_table
Listing channels ...
┌───────────────────────────────────────────┬────────────────┬───────────────────────┐
│ name                                      │ prefetch_count │ global_prefetch_count │
├───────────────────────────────────────────┼────────────────┼───────────────────────┤
│ 61.170.208.88:60591 -> 10.0.4.16:5672 (1) │ 0              │ 5                     │
├───────────────────────────────────────────┼────────────────┼───────────────────────┤
│ 61.170.208.88:60610 -> 10.0.4.16:5672 (1) │ 0              │ 0                     │
└───────────────────────────────────────────┴────────────────┴───────────────────────┘
[root@nullnull-os ~]# 
RabbitMQ:消费端限流 Java初识RabbitMQ一消费端限流 为什么要对消费端限流 假设一个场景,首先,我们 Rabbitmq 服务器积压了有上万条未处理的消息,我们随便打开一个消费端,巨量的消息就会瞬间全部推送过来,但是我们单个消费端是无法同时处理这么多消息的。 当数据量特别大的时候,我们对生产端限流肯定是不科学的,因为有时候并发量就是特别大,有时候并发量又特别少,我们无法约束生产端,这是用户的行为。所以我们应该对... 阅读详情

相关推荐

RabbitMQ高级特性-消费端限流

消费端限流 消息队列中囤积了大量的消息, 或者某些时刻生产的消息远远大于消费者处理能力的时候, 这个时候如果消费者一次取出大量的消息, 但是客户端又无法处理, 就会出现问题, 甚至可能导致服务崩溃, 所以需要对消费端进行限流 RabbitMQ提供了一种qos(服务质量保证)功能, 即在非自动确认消息的前提下, 如果一定数目的消息(通过consumer或者channel设置qos的值)未被确...

听雪楼 3469

如何rabbitMQ进行消费限速

限流场景下,生产者的速度>消费者的速度,如果我们按照ribbitMq的默认配置,能达到限速的效果么,答案是否定的 不禁就产生了以下几个疑问: 问题1:默认场景下rabbitMQ的消费方式是推还是拉呢? 问题2:需要哪些配置rabbitMQ才能实现限速效果呢? 问题3:消费端是单线程消费还是多线程消费呢,如何进行控制? 问题4:其他消息中间件又如何来实现的呢? 问题5:哪些场景适合推模式,哪些场景适合拉模式呢? 带着这些疑问,展开对rabbitMq的进一步的学习 问题1:默认场景下rabbitMQ的消费

embelfe_segge的博客 3068

RabbitMQ高级之消息限流与延时队列

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)最新整理电子书下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)[外链图片转存中…(img-y0xMTD8w-1710364557009)]最新整理电子书。

m0_57077948的博客 1125

Spring+directRabbitmq

文章目录directRabbitmq1、通过spring实现direct(直连)2、发送消息3、监听队列 参考文章链接 directRabbitmq 1、通过spring实现direct(直连) 配置文件,创建队列和交换机以及配置路由 @Configuration public class DirectRabbitConfig { //队列 起名:TestDirectQueue @Bean("testDirectQueue1") public Queue testDirectQue

qq_40950803的博客 456

基于Opencv+Mediapipe的头部姿态检测

在学生课堂专注度检测项目中,可以通过学生头部姿态来确定学生注意力方向,从而判断其是否专注于课堂。

weixin_67879842的博客 2335

长短期记忆网络(LSTM)预测模型及其Python和MATLAB实现

LSTM 通过独特的门控机制解决了传统 RNN 的梯度消失和梯度爆炸问题,使其在处理长序列时具有显著优势。随着技术的不断发展,LSTM 及其变种(如 Bi-directional LSTM、Stacked LSTM 等)有望在更复杂的任务中发挥重要作用。2. **可视化真实值和预测值**:通过绘图来比较真实值和预测值,判断模型的准确性。2. **输入门(Input gate)**:决定当前的输入信息对细胞状态的影响。1. **细胞状态(Cell state)**:保持信息在时间步之间的传递。

qq_45441438的博客 2055

RabbitMQ中限流操作

在RabbitMQ中为什么会有限流这种操作的存在呢? 想一下,如果生产端投递量几十万条数据,假如这些消息在队列里堆积了。当消费端去消费的时候瞬间涌上来,数据量那么大,消费端一时承受不了,很大可能会垮掉。因此为了减轻压力,需要使用限流操作。

程序和我有一个能跑就行了 9762

告别简陋行情!在Ubuntu24.04系统用Wine9.0跑同花顺远航版的全流程记录

本文详细记录了在Ubuntu 24.04系统上,通过Wine 9.0兼容层成功安装并流畅运行同花顺远航版的全过程。文章对比了该方案与官方简陋Linux原生版本的功能差异,重点解决了高分辨率屏幕下的字体与界面显示问题,并提供了关键配置调优与长期维护建议,为Linux用户提供了功能完备的专业金融行情解决方案。

weixin_29207533的博客 1072

RabbitMQ--基础--09--消费端限流策略

高并发情况下,队列里面一瞬间就就积累了上万条数据,但是消费者无法同时处理这么多请求,这个时候当我们打开客户端,瞬间就有巨量的信息给推送过来、但是客户端是没有办法同时处理这么多数据的,结果就是消费者(客户端)挂掉了。Consumer2 消费了 5 条消息,但它都没有确认,由于 channel.basicQos(5, false) 的缘故,它就没再消费消息了。Consumer 消费了 15 条消息,因为它每消费一条就 ACK 一条,另外 5 条消息被另一个 Consumer2 给持有。

zhou920786312的博客 1267

从仿真到实体:基于Isaac Gym的X20机械狗与Kinova Gen3 Lite机械臂全身协同控制实战

本文详细介绍了基于Isaac Gym仿真平台,实现云深处X20机械狗与Kinova Gen3 Lite机械臂全身协同控制的完整实战流程。内容涵盖环境搭建、模型适配、分阶段训练策略、域随机化技巧,以及最终将训练好的策略安全部署到实体机器人的关键步骤,为复杂机器人系统的仿真到实体迁移提供了宝贵经验。

yhn45678901的博客 489

SDA 还是 SDI:SAP HANA 数据虚拟化与数据集成的取舍、组合与实战

结论先放在前面:SDA 面向数据虚拟化与联邦查询,强调不落地、不复制、按需实时访问;SDI 面向数据集成与复制,强调抽取、转换、装载与近实时增量同步。两者共享 Remote Source 与 Adapter 的理念,但侧重点与运行时组件明显不同。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 583

【RabbitMQ】RabbitMQ高级:如何保证消息可靠性

RabbitMQ高级:如何保证消息可靠性

qq_43745578的博客 1685

华为OD机试双机位C卷-魔法收积木 (C/C++/Py/Java/Js/Go)

华为OD机试(机考)双机位C卷 【魔法收积木】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为od - 魔法收积木。

qq_45776114的博客 1939

Java 中间件:RabbitMQ 消费端限流(basicQos 配置)

Java 中间件:RabbitMQ 消费端限流实践 本文介绍了RabbitMQ消费端限流机制的核心原理与实现方法。主要内容包括: 基础概念:解释了RabbitMQ的推/拉模式、消息确认机制和预取计数(prefetchCount)的概念,强调限流只在手动确认模式下生效。 原理分析:详细解析basicQos方法的三种重载形式,重点说明prefetchCount和global参数的作用,以及它们在不同层级(通道/消费者)的应用范围。 实战配置:提供Java客户端的完整代码示例,展示如何建立连接、声明队列、设置ba

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

【图像去噪】论文精读:AKDT: Adaptive Kernel Dilation Transformer for Effective Image Denoising

论文题目:AKDT: Adaptive Kernel Dilation Transformer for Effective Image Denoising —— AKDT:用于有效图像去噪的自适应核膨胀Transformer图像去噪是一项基本但具有挑战性的任务,特别是在处理高分辨率图像和复杂的噪声模式时。大多数现有方法依赖于标准的 Transformer 架构,该架构通常计算复杂度高,并且对不同噪声水平的适应性有限。在本文中,我们介绍了自适应核膨胀Transformer(AKDT)

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1044

双重机器学习模型中解释变量是连续变量的代码,看到好多都是0/1变量,如何解决?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!    📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位一线资深架构师和工程师多年实战经验沉淀,提

**My Coding Family** 851

RabbitMQ如何实现消费端限流

在 RabbitMQ 中,可以通过消费者端限流(Consumer Prefetch)来控制消费端处理消息的速度,以避免消费端处理能力不足或处理过慢而导致消息堆积。消费者端限流的主要目的是控制消费者每次从 RabbitMQ 中获取的消息数量,从而实现消息处理的流量控制。RabbitMQ 提供了一种 QOS(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息还未被消费确认,则不进行新消息的消费。RabbitMQ 为我们提供了三种机制● 对内存和磁盘使用量设置阈值。

刘皇叔说Java的博客 2478

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

rabbitmq如何保证消息可靠性

个人博客请访问http://www.x0100.top 如何保证消息的可靠性? 消息队列如何进行限流? 如何设置延时队列进行延时消费? 1. ????如何保证消息的可靠性? 先来看看我们的万年老图,从图上我们大概可以看出来一个消息会经历四个节点,只有保证这四个节点的可靠性才能保证整个系统的可靠性。 生产者发出后保证到达了MQ。 MQ收到消息保证分发到了消息对应的Exchange。 Exchange分发消息入队之后保证消息...

HelloWorld搬运工 1410

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

RabbitMQ的高级特性--如何保证消息可靠性

1.rabbitMQ临时队列 1.首先,我们无论何时连接RabbitMQ的时候,都需要一个新的,空的队列。我们可以使用随机的名 字创建队列,也可以让服务器帮我们生成随机的消息队列名字。 其次,一旦我们断开到消费者的连接,该队列应该自动删除。 2.创建临时队列方式如下: String queue = channel.queueDeclare().getQueue(); 上述代码我们声明了一个非持久化的、排他的、自动删除的队列,并且名字是服务器随机生成的...

李嘉图呀李嘉图的博客 4347

消息中间件--RabbitMQ --- 消费端限流 -- 非常重要

什么是消费端的·限流? 假设一个场景,首先,我们Rabbitmq服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现下面的情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据! 注意:高并发情景下,生产端我们没办法做限制,所以我们只能限制消费端,防止消费端口资源耗尽。 RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定...

炫的博客 2万+

RabbitMQ消息可靠性解决方案(异常捕获、事务、发送端消息确认、消息持久化、消费者消息确认、消费端限流、消息可靠性保障、消息幂等性处理)

RabbitMQ消息可靠性解决方案(异常捕获、事务、发送端消息确认、消息持久化、消费者消息确认、消费端限流、消息可靠性保障、消息幂等性处理)

weixin_44795847的博客 1504

RabbitMQ高级特性——消费端限流,prefetch的设置

大伙可以到我的RabbitMQ专栏获取更多信息 demo示例这里拿 概述 在之前介绍的MQ的文章中——什么是MQ:消息队列的基本概念和优劣势,我提到了MQ对于处理高并发场景的能力,即削峰填谷: 当请求并发增高的时候,所有业务请求首先放入MQ 业务系统则在保证系统正常平稳的情况下,尽力从MQ获取业务信息,按照自己的“节奏”处理业务 过多的业务积压在MQ中,拉长了整体业务的执行时长,这就是填谷 上述过程中,第二步其实就是对消费端的限流,帮助MQ的各个消费者平稳的度过业务请求的高峰,其实还有一个场景.

保暖透气大裤衩LeoLee的博客 3742

Rabbitmq消息积压问题如何解决以及如何进行限流

一、增加处理能力优化系统架构、增加服务器资源、采用负载均衡等手段,以提高系统的处理能力和并发处理能力。通过增加服务器数量或者优化代码,确保系统能够及时处理所有的消息。二、异步处理将消息的处理过程设计为异步执行,即接收到消息立即返回响应,然后将消息放入队列中进行后续处理。这样可以避免同步请求的阻塞,提高系统的吞吐量和响应速度。三、消息分片如果消息体较大或者复杂,可以考虑将消息分片处理。将消息拆分为多个小的部分进行处理,减少单个消息的处理时间,从而提高整体处理能力。四、集群扩展。

weixin_43360488的博客 3106

RabbitMQ顺序性、可靠性、重复消费、消息堆积解决方案

RabbitMQ使用过程中,有些业务场景需要我们保证顺序消费,例如:业务上产生三条消息,分别是对数据的增加、修改、删除操作,如果没有保证顺序消费,执行顺序可能变成删除、修改、增加,这就乱了。RabbitMQ的消息顺序问题,需要分三个环节看待

Andy的博客 3538

MQ消费端限流

MQ三大主要功能:削峰、异步、解耦,这章主要是讲削峰,可以通过MQ消费端限流来实现。 消费端限流主要是为了保证消费端的服务稳定性,防止超过消费端负载,将服务打挂,卡死。 rabbitmq提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(给channel或者consume设置Qos值)未被确认前,不进行消费新消息。 使用: 1.在消费端,通过channel.basicQos方法进行设置 2.关闭自动ACK 测试 生产端: package com.vivo.demo1.co

qq_40911404的博客 2635

RabbitMQ消费端限流

消费端限流 1. 为什么要对消费端限流 假设一个场景,首先,我们 Rabbitmq 服务器积压了有上万条未处理的消息,我们随便打开一个消费者客户端,会出现这样情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据! 当数据量特别大的时候,我们对生产端限流肯定是不科学的,因为有时候并发量就是特别大,有时候并发量又特别少,我们无法约束生产端,这是用户的行为。所以我们应该对消费端限流,用于保持消费端的稳定,当消息数量激增的时候很有可能造成资源耗尽,以及影响服务的性能,导致系统的卡顿甚至直接崩

weixin_44407691的博客 538
上一篇: RabbitMQ消息可靠性保证机制3--消费端ACK机制
下一篇: RabbitMQ消息可靠性保证机制5--消息幂等性处理
null_null_bug
博客等级 码龄7年 72粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值