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

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


代码位置

https://gitee.com/DanShenGuiZu/learnDemo/tree/master/rabbitMq-learn/rabbitMq-03

1、channel.basicQos() 方法

  1. 设置消费者最多接收消息的个数
  2. 消费者端要把自动确认 autoAck 设置为 false,channel.basicQos() 方法才会有效果

1.1、重载方法

basicQos(int prefetchCount);
basicQos(int prefetchCount, boolean global);
basicQos(int prefetchSize, int prefetchCount, boolean global);

1.2、各个参数含义

1.2.1、prefetchSize

  1. 可接收消息的大小
  2. 如果设置为0,那么表示对消息本身的大小不限制

1.2.2、prefetchCount

  1. 处理消息最大的数量。相当于消费者能一次接受的队列大小
  2. 举例:
    1. 如果设置为6,也就是消费者能接受消息的最大队列大小为6,假设队列名称为q1
    2. 如果q1=6:消费者不能在接受MQ的消息
    3. 如果q1<6:消费者可以接受MQ的消息,直到q1=6

1.2.3、global

  1. 是不是针对整个 Connection 的,因为一个 Connection 可以有多个 Channel
  2. global=false:针对的是这个 Channel
  3. global=ture: 针对的是这个 Connection

2、代码实现(手动 ACK)

一个生产者生产了20条消息,有两个消费者:Consumer、Consumer2。并将这两个消费者的prefetchCount值 设置成5,Consumer 设置为 手动ACK,而 Consumer2 自动ACK

2.1、代码结构

在这里插入图片描述

2.2、生产者

package com.example.rabbitmq03.business.test8;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class Producer {

    public static void main(String[] args) {
        // 1. 获取连接
        Connection connection = null;
        try {
            connection = RabbitMqUtil.getConnection("生产者");
        } catch (Exception e) {
            System.out.println("获取连接时,出现异常");
        }

        Channel channel = null;
        try {
            // 2. 通过连接获取通道 Channel
            channel = connection.createChannel();
            String queueName = "code_simple_queue1";
            // 3. 通过通道创建声明队列
            channel.queueDeclare(queueName, false, false, false, null);
            // 4. 发送消息给队列 Queue
            for (int i = 0; i < 20; i++) {
                // 5. 准备消息内容
                String message = "你好 " + i;
                channel.basicPublish("", queueName, null, message.getBytes());
                System.out.println("消息发送完成~~~发送的消息为:" + message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                RabbitMqUtil.close(connection, channel);
            } catch (Exception e) {
                System.out.println("关闭时,出现异常");
            }
        }
    }
}


2.3、Consumer

package com.example.rabbitmq03.business.test8;

import java.io.IOException;

import com.rabbitmq.client.*;

public class Consumer {
    
    public static void main(String[] args) throws Exception {
        // 获取连接
        Connection connection = RabbitMqUtil.getConnection("消费者");
        
        final Channel channel = connection.createChannel();
        String queueName = "code_simple_queue1";
        
        // 定义消费者
        com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                
                // 消息id,mq 在 channel 中用来标识消息的 id,可用于确认消息已接收
                long deliveryTag = envelope.getDeliveryTag();
                // body 消息体
                String msg = new String(body, "utf-8");
                System.out.println("收到消息:" + msg);
                /**
                 * @param1:deliveryTag:用来标识消息的id
                 * @param2:multiple:是否批量。true:将一次性 ACK 所有小于 deliveryTag 的消息
                 */
                // 手动确认
                channel.basicAck(deliveryTag, false);
            }
        };
        
        // 限制未 ACK 的消息最大数
        channel.basicQos(5, false);
        // 监听队列 手动 ACK
        channel.basicConsume(queueName, false, consumer);
        
        System.out.println("开始接收消息~~~");
        System.in.read();
        
        // 关闭信道、连接
        RabbitMqUtil.close(connection, channel);
    }
}


2.3、Consumer2

Consumer2和Consumer唯一区别就是注释了 手动ACK代码

package com.example.rabbitmq03.business.test8;

import java.io.IOException;

import com.rabbitmq.client.*;

public class Consumer2 {
    
    public static void main(String[] args) throws Exception {
        // 获取连接
        Connection connection = RabbitMqUtil.getConnection("消费者");
        
        final Channel channel = connection.createChannel();
        String queueName = "code_simple_queue1";
        
        // 定义消费者
        com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                // 消息id,mq 在 channel 中用来标识消息的 id,可用于确认消息已接收
                long deliveryTag = envelope.getDeliveryTag();
                // body 消息体
                String msg = new String(body, "utf-8");
                System.out.println("收到消息:" + msg);
                /**
                 * @param1:deliveryTag:用来标识消息的id
                 * @param2:multiple:是否批量。true:将一次性 ACK 所有小于 deliveryTag 的消息
                 */
                // 手动确认
                // channel.basicAck(deliveryTag, false);
                // 它只消费消息,并未确认消息。因为我们这里没有设置手动确认
            }
        };
        
        // 限制未 ACK 的消息最大数
        channel.basicQos(5, false);
        // 监听队列 手动 ACK
        channel.basicConsume(queueName, false, consumer);
        
        System.out.println("开始接收消息~~~");
        System.in.read();
        
        // 关闭信道、连接
        RabbitMqUtil.close(connection, channel);
    }
}

2.4、测试

2.4.1、Producer 创建20条消息

在这里插入图片描述

2.4.2、Consumer

Consumer 消费了 15 条消息,因为它每消费一条就 ACK 一条,另外 5 条消息被另一个 Consumer2 给持有。

在这里插入图片描述

2.4.3、Consumer2

Consumer2 消费了 5 条消息,但它都没有确认,由于 channel.basicQos(5, false) 的缘故,它就没再消费消息了。所有MQ里面有5条没有确认的消息

在这里插入图片描述

在这里插入图片描述

3、限流策略

3.1、场景

高并发情况下,队列里面一瞬间就就积累了上万条数据,但是消费者无法同时处理这么多请求,这个时候当我们打开客户端,瞬间就有巨量的信息给推送过来、但是客户端是没有办法同时处理这么多数据的,结果就是消费者(客户端)挂掉了

3.2、对消费端进行限流

channel.basicQos(int prefetchSize, int prefetchCount, boolean global);

# 对消息本身的大小不限制,但是同一时刻最大只能接受10条消息。
channel.basicQos(0, 10, false);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值