work消息模型【能者多劳】
不同消费者可能消费消息的速度不一样,work模型通过手动确认机制实现能者多劳
多个消费者通过自动确认机制
工具类
public class RabbitMQUtil {
private static ConnectionFactory connectionFactory;
static {
//创建连接工厂
connectionFactory = new ConnectionFactory();
//设置RabbitMQ服务器所在主机ip
connectionFactory.setHost("www.onething.top");
//设置端口号
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("123456nw");
}
public static Connection getConnection() {
//创建连接对象
try {
return connectionFactory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void closeConnectionAndChannel(Channel channel, Connection connection) {
try {
if(channel!=null){
channel.close();
}
if(connection!=null){
connection.close();
}
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
}
工人队列消息生产者
public class WorkQueueDemo {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
for (int i = 0; i < 20; i++) {
channel.basicPublish("", "work", MessageProperties.PERSISTENT_TEXT_PLAIN, ("生产者" + i).getBytes());
}
RabbitMQUtil.closeConnectionAndChannel(channel,connection);
}
}
工人队列消费者A
public class WorkQueueConsumerADemo {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtil.getConnection();
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("consumerA:"+new String(body));
}
});
}
}
工人队列消费者B
public class WorkQueueConsumerBDemo {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtil.getConnection();
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 {
try {
Thread.sleep(1000); // 消费者A与消费者B的区别
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("consumerB:"+new String(body));
}
});
}
}
设置为自动确认机制时,消费者A与消费者B消费结果对比如下:
**总结:**默认情况下,RabbitMQ按顺序将消息发送给每个消费者,即使每个消费者消费者消费消息的速度不同,但每个消费者收到的消息数量相同,这种消费方式为轮询。
自动确认机制时,当消费者连接上队列,如果没有指定消费者一次获取消息的条数,所以队列把队列中的所有消息一下子推送到消费者端,当消息从队列被推出时的那一刻就表示已经对消息进行自动确认了,消息就会从队列中被标记为删除,至于消费者是否消费完已经收到的消息,就不得而知了。当消费者未处理完收到的消息时,重启RabbitMQ,此时就会出现消息丢失。因此开发过程中不建议使用自动确认机制
RabbitMQ的确认模式
参考文章:RabbitMQ消息确认机制之Confirm模式总结
(1)自动确认
只有消费者从队列中获取了消息,无论是否消费成功,都认为消息已经被消费,队列会把该消息数据删掉
(2)手动确认
消费者从队列获取消息后,MQ服务器会将该消息标记为不可用,等待消费者反馈。如果一直没有反馈,则一直未不可用状态,该消息不会被删除也不能被消费
当出现Unacked的消息时,只需要断开连接或重启RabbitMQ,被标记Unacked状态的消息就会重新变为Ready状态
public class WorkQueueConsumerADemo {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
/**
* 每次只能消费一个消息【切记这里需要设置】
*/
channel.basicQos(1);
channel.queueDeclare("work", true, false, false, null);
/**
* 第1个参数:队列名
* 第2个参数:自动确认
* 第3个参数:回调方法,用于处理消息
*/
channel.basicConsume("work", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerA:" + new String(body));
/**
* 手动确认
* 第1个参数:通过发送Tag标识来确认消费的是消息队列中的哪个消息
* 第2个参数:是否开启多个消息同时确认,这里我们设置了每次只能消费一个消息,因此不需要开启
*/
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
public class WorkQueueConsumerBDemo {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
/**
* 每次只能消费一个消息【切记这里需要设置】
*/
channel.basicQos(1);
channel.queueDeclare("work", true, false, false, null);
/**
* 第1个参数:队列名
* 第2个参数:自动确认
* 第3个参数:回调方法,用于处理消息
*/
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(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("consumerA:" + new String(body));
/**
* 手动确认
* 第1个参数:通过发送Tag标识来确认消费的是消息队列中的哪个消息
* 第2个参数:是否开启多个消息同时确认,这里我们设置了每次只能消费一个消息,因此不需要开启
*/
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
设置为手动确认机制后,消费者A与消费者B消费结果对比如下: