上一篇,用springboot整合了rabbitmq,并且成功实现了发送者发送消息,消费者消费消息,这一篇实现工作队列的功能
@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条消息
在控制台的输出中可以看出,两个消费者轮流去消费队列的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);
}
}
看控制台的输出
从控制台输出可以看出,两个发送者轮流发送消息到队列中,两个消费者轮流去队列消费消息
由此可得出结论: 无论多少个发送者发送了多少消息到队列,消费者们会依次轮流去消费消息
其实轮循分发存在一个很大的问题,在现实中往往是一些消息消费起来时间很快,而一些消息消费起来却很耗时,因此,如果采用轮循分发则会造成一些消费者一直在工作,而一些消费者则是一直处于空闲中。因此,我们往往用的是公平分发的机制,所谓公平分发,则是指消费者一次只消费一条消息,只有当前消息消费完了,才会去请求新的消息。下面来看看实现(实例中假设消费者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);
}
}
控制台输出:
从控制台可看出,一开始由于消费者2消费每条消息都要睡眠上10ms,所以明显消费者1消费的消息要多与消费者2,而到了第50条消息的时候,由于消费者1要睡眠500ms,在这段时间内,都是由消费者2来消费消息,因此实现了公平转发