RabbitMQ入坑系列(三):工作队列

上一篇,用springboot整合了rabbitmq,并且成功实现了发送者发送消息,消费者消费消息,这一篇实现工作队列的功能

轮循分发

一对多(一个发送者,两个消费者)

RabbitConfig

@Configuration
public class RabbitConfig {
    @Bean
    public Queue queue(){
        return new Queue("hello");
    }

    @Bean
    public Queue queue2(){
        return new Queue("hello2");
    }
}

基于上一篇的rabbitconfig,新增一个hello2的队列

发送者

package com.fandy.rabbitmq.component;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Author: Fandy
 * @Date: 2020/7/16 13:54
 */
@Component
public class Sender {

    @Autowired
    @SuppressWarnings("all")
    private AmqpTemplate amqpTemplate;

    //一对一
    public void send(){
        String msg = "hello";
        System.out.println("sender:"+msg);
        amqpTemplate.convertAndSend("hello",msg);
    }

    //一对多
    public void send2(int i){
        String msg = "第"+i+"个消息";
        System.out.println(msg);
        amqpTemplate.convertAndSend("hello2",msg);
    }

}

还是上一篇的sender.class,在类中新增一个发送方法send2

消费者

消费者1:

@Component
@RabbitListener(queues = "hello2")
public class ManyReceiver1 {
    @RabbitHandler
    public void receive1(String msg){
        System.out.println("receive1:"+msg);
    }

}

消费者2:

@Component
@RabbitListener(queues = "hello2")
public class ManyReceiver2 {
    @RabbitHandler
    public void receive1(String msg){
        System.out.println("receive2:"+msg);
    }

}

测试

@Test
    public void oneToMany(){
        for (int i=0;i<100;i++) {
            sender.send2(i);
        }
    }

让发送者发送100条消息,让两个消费者消费这100条消息

控制台

RabbitMQ入坑系列(三):工作队列_第1张图片
在控制台的输出中可以看出,两个消费者轮流去消费队列的100条消息,消费者1消息基数的消息,消费者2消费偶数的消息

多对多(两个发送者,两个消费者)

新增一个发送者sender2

@Component
public class Sender2 {
    @Autowired
    AmqpTemplate amqpTemplate;

    public void send2(int i){
        String msg = "第"+i+"个消息";
        System.out.println("sender2:"+msg);
        amqpTemplate.convertAndSend("hello2",msg);
    }
}

写一个测试方法,让测试方法轮流让两个发送者发送100条消息

@Test
    public void ManyToMany(){
        for (int i=0;i<100;i++) {
            sender.send2(i);
            sender2.send2(i);
        }
    }

看控制台的输出
RabbitMQ入坑系列(三):工作队列_第2张图片
从控制台输出可以看出,两个发送者轮流发送消息到队列中,两个消费者轮流去队列消费消息
由此可得出结论: 无论多少个发送者发送了多少消息到队列,消费者们会依次轮流去消费消息

公平分发

其实轮循分发存在一个很大的问题,在现实中往往是一些消息消费起来时间很快,而一些消息消费起来却很耗时,因此,如果采用轮循分发则会造成一些消费者一直在工作,而一些消费者则是一直处于空闲中。因此,我们往往用的是公平分发的机制,所谓公平分发,则是指消费者一次只消费一条消息,只有当前消息消费完了,才会去请求新的消息。下面来看看实现(实例中假设消费者2每次消费消息都要花费10ms的时间,而在第50条消息的时候,消费者1要耗费500ms的时间)

发送者:

//一对多
    public void send3(int i){
        String msg = "第"+i+"个消息";
        System.out.println("sender3:"+msg);
        amqpTemplate.convertAndSend("hello3",msg);
    }

消费者:

package com.fandy.rabbitmq.component;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @Author: Fandy
 * @Date: 2020/7/16 17:44
 */
@Component
public class Receiver3 {
    /*@Bean("workListenerFactory")
    public RabbitListenerContainerFactory myFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory containerFactory =
                new SimpleRabbitListenerContainerFactory();
        containerFactory.setConnectionFactory(connectionFactory);
        //自动ack,没有异常的情况下自动发送ack
        //auto  自动确认,默认是auto
        //MANUAL  手动确认
        //none  不确认,发完自动丢弃
        containerFactory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //拒绝策略,true回到队列 false丢弃,默认是true
        containerFactory.setDefaultRequeueRejected(true);

        //默认的PrefetchCount是250,采用Round-robin dispatching,效率低
        //setPrefetchCount 为 1,即可启用fair 转发
        containerFactory.setPrefetchCount(1);
        return containerFactory;
    }*/

    /**
     * 若不使用自定义containerFactory = "workListenerFactory",默认的轮询消费效率低
     *
     * @param s
     */
    @RabbitListener(queuesToDeclare = @Queue(value = "hello3",autoDelete = "false"), containerFactory = "workListenerFactory")
    public void workQueue1(String s) throws InterruptedException {
        if("第50个消息".equals(s)){
            Thread.sleep(500);
        }
        System.out.println("workQueue 1  " + s);
    }

    @RabbitListener(queuesToDeclare = @Queue(value = "hello3",autoDelete = "false"), containerFactory = "workListenerFactory")
    public void workQueue2(String s) throws InterruptedException {
        Thread.sleep(10);
        System.out.println("workQueue 2  " + s);
    }
}

这里设置消费者1如果消费到50个消息的时候,让其睡眠500ms,而消费者2则是每条消息都睡眠10ms

配置RabbitListenerContainerFactory:

@Component
public class GetRabbitListenerContainerFactory {

    @Bean("workListenerFactory")
    public RabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        //自动ack,没有异常的情况下自动发送ack
        //auto  自动确认,默认是auto
        //MANUAL  手动确认
        //none  不确认,发完自动丢弃
        factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //拒绝策略,true回到队列 false丢弃,默认是true
        factory.setDefaultRequeueRejected(true);
        //默认的PrefetchCount是250,采用Round-robin dispatching,效率低
        //setPrefetchCount 为 1,即可启用fair 转发
        factory.setPrefetchCount(1);
        return factory;
    }
}

配置RabbitListenerContainerFactory,设置prefetchCount为1,则是采取公平转发的机制
测试:

@Test
    public void Send3(){
        for (int i=0;i<100;i++) {
            sender.send3(i);
        }
    }

控制台输出:
RabbitMQ入坑系列(三):工作队列_第3张图片RabbitMQ入坑系列(三):工作队列_第4张图片
从控制台可看出,一开始由于消费者2消费每条消息都要睡眠上10ms,所以明显消费者1消费的消息要多与消费者2,而到了第50条消息的时候,由于消费者1要睡眠500ms,在这段时间内,都是由消费者2来消费消息,因此实现了公平转发

你可能感兴趣的:(RabbitMQ)