RabbitMQ通信方式之Work queues
Work Queues通信方式结构图

Work Queues与Hello World异同点:
Hello World是一个生产者和一个消费者。
Work Queues是一个生产者和多个消费者。
Work Queues和Hello World的形式是一样的,都是将消息推送到默认交换机。
RabbitMQ会将消息以循环的方式发送给多个消费者,以平均负载。
添加RabbitMQ和Junit工具栏的maven依赖
<!--rabbitmq-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.12.0</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
创建一个构建连接的工具类
public class RabbitMQConnectionUtil {
public static final String RABBITMQ_HOST = "xxx.xx.xxx.xxx"; //设置你自己的RabbitMQ服务ip
public static final int RABBITMQ_PORT = 5672;
public static final String RABBITMQ_USERNAME = "guest";
public static final String RABBITMQ_PASSWORD = "guest";
public static final String RABBITMQ_VIRTUAL_HOST = "/";
/**
* 构建RabbitMQ的连接对象
*/
public static Connection getConnection() throws Exception {
//1. 创建Connection工厂
ConnectionFactory factory = new ConnectionFactory();
//2. 设置RabbitMQ的连接信息
factory.setHost(RABBITMQ_HOST);
factory.setPort(RABBITMQ_PORT);
factory.setUsername(RABBITMQ_USERNAME);
factory.setPassword(RABBITMQ_PASSWORD);
factory.setVirtualHost(RABBITMQ_VIRTUAL_HOST);
//3. 返回连接对象
Connection connection = factory.newConnection();
return connection;
}
创建生产者
public class Producer {
public final static String QUEUE_NAME = "hello";
@Test
public void publish() throws Exception {
// 创建连接工厂
Connection connection = RabbitMQConnectionUtil.getConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 循环发送10次消息
for (int i = 0; i < 10; i++) {
String message = "Hello World!"+i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
}
System.out.println("消息发送成功!");
}
}
创建多个消费者
public class Consumer {
/**
* 消费者1号
*/
@Test
public void consume1() throws Exception {
//1. 获取连接对象
Connection connection = RabbitMQConnectionUtil.getConnection();
//2. 构建Channel
Channel channel = connection.createChannel();
//3. 构建队列
channel.queueDeclare(Producer.QUEUE_NAME,false,false,false,null);
//3.5 设置消息的流控
channel.basicQos(1);
//4. 监听消息
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
//睡0.1s来模拟该消费者消费能力较强
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("消费者1号获取到消息:" + new String(body,"UTF-8"));
//手动ack(确认消息已经被处理)
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//将第二个参数设为false意为关闭自动ack
channel.basicConsume(Producer.QUEUE_NAME,false,callback);
System.out.println("1号开始监听队列");
System.in.read();
}
/**
* 消费者2号
*/
@Test
public void consume2() throws Exception {
//1. 获取连接对象
Connection connection = RabbitMQConnectionUtil.getConnection();
//2. 构建Channel
Channel channel = connection.createChannel();
//3. 构建队列
channel.queueDeclare(Producer.QUEUE_NAME,false,false,false,null);
//3.5 设置消息的流控
channel.basicQos(1);
//4. 监听消息
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
//睡0.5s来模拟该消费者消费能力较弱
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("消费者2号获取到消息:" + new String(body,"UTF-8"));
//手动ack(确认消息已经被处理)
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//将第二个参数设为false意为关闭自动ack
channel.basicConsume(Producer.QUEUE_NAME,false,callback);
System.out.println("2号开始监听队列");
System.in.read();
}
}
开启两个消费者以及生产者
观察消费1号状态

观察消费2号状态

上述例子可以看到,多个消费者监听同一个队列时,我们可以通过手动ack加流控的方式控制它们的消费能力。
自动ack和手动ack是RabbitMQ中确认消息的两种方式:
自动ack:当消费者从队列中取出消息并开始处理时,RabbitMQ会立即确认该消息,即将其从队列中删除,无需等待消费者处理完成。这种方式适合于消息处理时间很短、消息丢失不会产生严重后果的场景,可以提高消息处理的吞吐量。
手动ack:消费者在处理完一条消息后,需要向RabbitMQ发送一个确认消息的指令,以告诉RabbitMQ该消息已经被成功处理。如果消费者在处理消息时出现了异常,那么就不会发送确认消息,这样RabbitMQ会重新将该消息发送给其他消费者进行处理。手动ack的好处是可以确保消息不会丢失,同时也可以控制消息的处理顺序和速度。但是,如果消费者在处理消息时出现了异常,就需要考虑如何处理未确认的消息,避免消息丢失或者重复消费的问题。