一、什么是工作队列模型
work queues,也称为(Task queues)任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用work模型:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。
二、实现代码
生产者:
package workQuene;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wmh
* @date 2021/1/11
*/
public class Provider {
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 连接mq服务
connectionFactory.setHost("主机名");
connectionFactory.setPort(5672);
// 连接虚拟主机
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
// 获取连接
Connection connection = connectionFactory.newConnection();
// 创建连接通道
Channel channel = connection.createChannel();
// 为通道绑定消息队列
channel.queueDeclare("work",true,false,false,null);
// 发布消息
for (int i = 0; i < 50; i++) {
channel.basicPublish("","work",null,(i+"work quene").getBytes());
}
// 关闭连接
channel.close();
connection.close();
}
}
消费者(两个消费者代码一样只写其中一个)
package workQuene;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wmh
* @date 2021/1/11
*/
public class Customer2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 连接mq服务
connectionFactory.setHost("主机名");
connectionFactory.setPort(5672);
// 连接虚拟主机
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
// 获取连接
Connection connection = connectionFactory.newConnection();
// 创建连接通道
final Channel channel = connection.createChannel();
// 为通道绑定消息队列
channel.queueDeclare("work",true,false,false,null);
// 消费消息
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Coustomer2消费了消息"+new String(body));
}
});
}
}
结果:
总结:从运行结果可以看出,默认情况下,RabbitMQ将按顺序将每个消息发送给下一个使用者。平均而言,每个消费者都会收到相同数量的消息
但是当某个消费者处理速度较慢时,仍然是这种分发规则并不会因为某个消费者消费速度快而分发更多的任务。例如当我在消费者1设置每次处理消息时休眠1秒,两个消费者仍然处理是通过轮询的方式来分配任务,这就导致消费者2处理完消息处于空闲状态而消费者1还在处理消息。
平均分配的原因:
消息自动确认机制:在channel.basicConsume的第二个参数设置,当设置为true时,消费者拿到消息后不管消费者内的消息是否处理完成,都会自动向队列确认消息已经消费完成进而获取新的消息,这就导致了即使消费者处理消息速度较慢还是会获取和其他消费者一样的消息。而当消费者出现故障宕机时,未消费完的消息会丢失(已经从队列删除消费者还未消费)
不使用消息自动确认机制的代码(能者多劳)
package workQuene;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wmh
* @date 2021/1/11
*/
public class Customer1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 连接mq服务
connectionFactory.setHost("主机名");
connectionFactory.setPort(5672);
// 连接虚拟主机
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
// 获取连接
Connection connection = connectionFactory.newConnection();
// 创建连接通道
final Channel channel = connection.createChannel();
// 为通道绑定消息队列
channel.queueDeclare("work",true,false,false,null);
// 设置通道每次只能消费一个消息
// 当设置为1时,消费者每次最大的未应答(确认)消息数为1,即每次都需要等到消息应答之后队列才会发送新的消息给消费者
// 若设置为2表示消费者未确认的消息数为2,即消费者即使上一条消息未确认也可以接收新的一条消息
/*
* channel.basicQos(1);与channel.basicAck(envelope.getDeliveryTag(),false);一起使用
* basicAck可以放在消息业务处理代码最后面,当消息消费完成后我们再手动确认消息已经消费,这时才可以接收到新的消息
* 从而实现能者多劳
* */
channel.basicQos(1);
// 消费消息
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Coustomer1消费了消息"+new String(body));
// 参数1:确认是队列中的那个具体消息 参数2:是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
结果: