02RabbitMq--轮询分发和公平分发两种分发方式

本文探讨了消息队列中两种常见的消息分发策略:轮询分发与公平分发。通过对比这两种分发方式的特点和实际运行效果,展示了如何通过设置不同的参数实现更高效合理的消息处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.轮询分发

队列给每一个消费者都发送一样的量的数据。
以下为例:
生产者发送数据:

public class Send
{
    //队列名称
    private static final String QUEUE_NAME = "QUEUE1";
    
    public static void main(String[] args)
    {
        try
        {
            //获取连接
            Connection connection = ConnectionFactoy.newConnection("生产者");
            //从连接中获取一个通道
            Channel channel = connection.createChannel();
            //声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            for (int i = 0; i < 10; i++)
            {
                String message = "这是第" + i+"次发送消息";
                System.out.println("[send]:" + message);
                //发送消息
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes("utf-8"));
                Thread.sleep(20 * i);
            }
            channel.close();
            connection.close();
        }
        catch (IOException | TimeoutException | InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

运行结果:
[send]:这是0次发送消息
[send]:这是1次发送消息
[send]:这是2次发送消息
[send]:这是3次发送消息
[send]:这是4次发送消息
[send]:这是5次发送消息
[send]:这是6次发送消息
[send]:这是7次发送消息
[send]:这是8次发送消息
[send]:这是9次发送消息

消费者1:

public class Receive1
{
    //队列名称
    private static final String QUEUE_NAME = "QUEUE1";
    
    public static void main(String[] args)
    {
        try
        {
            //获取连接
            Connection connection = ConnectionFactory.new Connection();
            //从连接中获取一个通道
            Channel channel = connection.createChannel();
            //声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //定义消费者
            DefaultConsumer consumer = new DefaultConsumer(channel)
            {
                //当消息到达时执行回调方法
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException
                {
                    String message = new String(body, "utf-8");
                    System.out.println("这是接收的第" + message);
                    try
                    {
                        //消费者休息1s处理业务
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                       
                    }
                }
            };
            //监听队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
    }
}

运行结果:
这是接收的第:这是0次发送消息
这是接收的第:这是2次发送消息
这是接收的第:这是4次发送消息
这是接收的第:这是6次发送消息
[这是接收的第:这是8次发送消息

消费者2:

public class Receive2
{
    //队列名称
    private static final String QUEUE_NAME = "QUEUE1";
    
    public static void main(String[] args)
    {
        
        try
        {
            //获取连接
            Connection connection = ConnectionFactory.new Connection();
            //从连接中获取一个通道
            Channel channel = connection.createChannel();
            //声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //定义消费者
            DefaultConsumer consumer = new DefaultConsumer(channel)
            {
                //当消息到达时执行回调方法
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException
                {
                    String message = new String(body, "utf-8");
                    System.out.println("这是接收的第" + message);
                    try
                    {
                        //消费者休息2s处理业务
                        Thread.sleep(2000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                                            }
                }
            };
            //监听队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
    }
}

运行结果:
这是接收的第:这是1次发送消息
这是接收的第:这是3次发送消息
这是接收的第:这是5次发送消息
这是接收的第:这是7次发送消息
[这是接收的第:这是9次发送消息

总结从两个消费者消费消息的模式来看,不难理解默认的轮询分发方式,就是采取一人一条的顺序执行来消费消息的。两个消费者得到的消息数量是相等的,从运行时可以看出消费者1先消费完,然后才轮到消费者2消费。并不会因为两个消费者的处理速度不一样使得两个消费者处理的消费信息量不一样。从mq界面可以看到,虽然mq中的消息很快就没有了,但是消费者依然在处理消息,这种轮询的分发方式存在着很大的隐患。

2.公平分发

消费者设置每次从队列里取出一条数据,并且关闭自动回复机制,每次取出一条数据,手动回复并继续取下一条数据。
代码测试:
生产者代码同上:
消费者1:

public class Receive1
{
    //队列名称
    private static final String QUEUE_NAME = "QUEUE_NAME1;
    
    public static void main(String[] args)
    {
        
        try
        {
            //获取连接
            Connection connection = ConnectionUtil.getConnection();
            //从连接中获取一个通道
            final Channel channel = connection.createChannel();
            //声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //设置每次从队列里取一条数据
            int prefetchCount = 1;
            channel.basicQos(prefetchCount);
            //定义消费者
            DefaultConsumer consumer = new DefaultConsumer(channel)
            {
                //当消息到达时执行回调方法
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException
                {
                    String message = new String(body, "utf-8");
                    System.out.println("接收的第" + message);
                    try
                    {
                        //消费者休息1s处理业务
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                     
                        //手动应答
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };
            //设置手动应答
            boolean autoAck = false;
            //监听队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
    }
}

运行结果:
这是接收的第:这是1次发送消息
这是接收的第:这是2次发送消息
这是接收的第:这是4次发送消息
这是接收的第:这是5次发送消息
这是接收的第:这是6次发送消息
这是接收的第:这是7次发送消息
这是接收的第:这是9次发送消息

消费者2:

public class Receive2
{
    //队列名称
    private static final String QUEUE_NAME = "QUEUE_NAME1";
    
    public static void main(String[] args)
    {
        
        try
        {
            //获取连接
            Connection connection = ConnectionFactory.new Connection();
            //从连接中获取一个通道
             Channel channel = connection.createChannel();
            //声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //保证一次只分发一个  
            int prefetchCount = 1;
            channel.basicQos(prefetchCount);
            //定义消费者
            DefaultConsumer consumer = new DefaultConsumer(channel)
            {
                //当消息到达时执行回调方法
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException
                {
                    String message = new String(body, "utf-8");
                    System.out.println("接收消息第" + message);
                    try
                    {
                        //消费者休息2s处理业务
                        Thread.sleep(2000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                                              //手动应答
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                }
            };
            //设置手动应答
            boolean autoAck = false;
            //监听队列
            channel.basicConsume(QUEUE_NAME, autoAck, consumer);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        
    }
}

运行结果:
这是接收的第:这是3次发送消息
这是接收的第:这是8次发送消息

总结
消费者1处理了8条消息,消费者2处理了2条消息。
与轮询不同的是,当每个消费都设置了每次只会从队列中取一条数据时,而且关闭自动应答,在每次处理完数据后手动给队列发送确认收到的数据,这样队列就会公平给消费者发送数据。而且在管理界面可以看到消费完数据而减少的,而不是一下子全部发送完。显然公平分发更符合设计规范。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值