基础知识
小知识
什么是RabbitMQ?
RabbitMQ是一个开源的消息代理软件,它实现了高级消息队列协议(AMQP)的标准。作为一个消息代理,RabbitMQ负责接收、存储和转发消息。它能够将消息从一个应用程序传递到另一个应用程序,以实现分布式系统之间的通信。
RabbitMQ具有以下主要功能和特性:
- 消息队列: RabbitMQ允许应用程序在生产者和消费者之间传递消息,消息可以在队列中暂存,直到消费者准备好处理它们。
- 消息路由: RabbitMQ支持不同类型的交换器,如直接交换器、扇出交换器、主题交换器等,这些交换器用于将消息路由到一个或多个队列中。
- 消息确认机制: RabbitMQ支持消息的确认机制,消费者可以确认收到消息并已处理完成,以确保消息的可靠传递。
- 持久化: RabbitMQ支持将消息持久化到磁盘,以防止消息丢失,即使在服务器重启后也能够保留消息。
- 灵活的集群和高可用性: RabbitMQ可以配置为以集群模式运行,从而提供高可用性和负载均衡。它还支持镜像队列,可以将队列的消息复制到多个节点上,以确保消息的可靠性和持久性。
- 插件系统: RabbitMQ提供了丰富的插件系统,可以通过插件来扩展其功能,比如管理插件、监控插件、认证插件等。
RabbitMQ广泛应用于各种场景,包括微服务架构、消息通知、日志收集、任务分发等。它是一个强大而灵活的消息代理,为构建可靠的分布式系统提供了重要的基础设施。
练习示例
RabbitMQ入门示例Java
- pom依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.14.1</version> <!-- 替换为最新版本 -->
</dependency>
- 发布消息
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitMQProducer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // RabbitMQ服务器地址
// 创建连接
try (Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel()) {
// 声明一个队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 发布消息到队列
String message = "Hello, RabbitMQ!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
}
}
}
- 消费消息
import com.rabbitmq.client.*;
public class RabbitMQConsumer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // RabbitMQ服务器地址
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 创建消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws java.io.IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
// 开始消费消息
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
- 小解析
- channel.queueDeclare(QUEUE_NAME, false, false, false, null) 方法中的五个参数分别表示:
1. queue: 队列名称,即要声明的队列的名称。
2. durable: 是否持久化队列。如果设置为true,则RabbitMQ服务器将在停机时保留队列,使其在重新启动时不会丢失。如果设置为false,则队列将不会持久化,即在RabbitMQ服务器停机时会丢失队列。默认为false。
3. exclusive: 是否为排他队列。如果设置为true,则只有声明队列的连接可以使用该队列。一旦连接关闭,队列将被删除。默认为false。
4. autoDelete: 是否自动删除队列。如果设置为true,则在最后一个消费者断开连接后,队列将被自动删除。默认为false。
5. arguments: 队列的其他属性。这是一个Map类型的参数,可以用于传递额外的参数。通常情况下,你可以将它设置为null,表示不需要传递额外的参数
- channel.basicPublish("", QUEUE_NAME, null, message.getBytes()); 这行代码中,有四个参数,它们分别代表:
1. exchange: 交换机名称。在消息被发送到队列之前,它被发送到交换机。RabbitMQ中的交换机是消息的路由器,它决定了消息应该被发送到哪个队列。在这个例子中,我们将它设置为空字符串,表示使用默认的交换机。
2. routingKey: 路由键。它是用于将消息路由到特定队列的关键字。在直接交换机(Direct Exchange)中,路由键与队列的名称相匹配。在扇出交换机(Fanout Exchange)中,路由键被忽略。在此示例中,我们使用队列的名称作为路由键,因为我们使用的是默认的直接交换机。
3. props: 消息的属性。它是一个可选参数,可以设置消息的各种属性,例如消息的持久性、优先级等。在此示例中,我们将其设置为null,表示不设置任何消息属性。
4. body: 消息的内容。它是一个字节数组,即消息的实际内容。在此示例中,我们将消息内容设置为字符串message的字节数组。
- public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)中四个参数的意义:
1. consumerTag: 消费者标签。当你通过 basicConsume 方法注册一个消费者时,你可以指定一个消费者标签。这个标签就是用来唯一标识一个消费者的。在多个消费者同时监听同一个队列的情况下,可以通过这个标签来区分不同的消费者。
2. envelope: 信封。它包含了与消息投递相关的元数据信息,比如交换机、路由键、投递标记等。具体的含义如下:
envelope.getDeliveryTag(): 消息的投递标记。用来唯一标识一个消息,一般情况下是一个递增的整数。在消息确认(ack)时会用到。
envelope.getExchange(): 消息被发送到的交换机名称。
envelope.getRoutingKey(): 消息的路由键。
3. properties: 消息的属性。它包含了消息的各种属性信息,比如消息的类型、内容编码、优先级等。具体的属性可以通过 properties 对象的方法来获取。
4. body: 消息的内容。它是一个字节数组,存储了消息的实际内容。需要根据消息的编码方式来进行解码,比如 UTF-8 或者其它编码方式。
- consumerTag举例
import com.rabbitmq.client.*;
public class MultipleConsumers {
private static final String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // RabbitMQ服务器地址
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 创建消费者1,并指定消费者标签为"consumer1"
String consumerTag1 = "consumer1";
Consumer consumer1 = createConsumer(channel, consumerTag1);
// 创建消费者2,并指定消费者标签为"consumer2"
String consumerTag2 = "consumer2";
Consumer consumer2 = createConsumer(channel, consumerTag2);
// 开始消费消息
channel.basicConsume(QUEUE_NAME, true, consumerTag1, consumer1);
channel.basicConsume(QUEUE_NAME, true, consumerTag2, consumer2);
}
// 创建消费者的辅助方法
private static Consumer createConsumer(Channel channel, String consumerTag) {
return new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws java.io.IOException {
String message = new String(body, "UTF-8");
System.out.println(" [" + consumerTag + "] Received '" + message + "'");
}
};
}
}
在这个例子中,我们创建了两个消费者,并为它们分别指定了不同的消费者标签,分别是consumer1和consumer2。然后,我们通过basicConsume方法将这两个消费者注册到同一个队列中。当有消息到达队列时,我们可以通过消费者标签来区分哪个消费者处理了该消息。
- 消息确认机制举例
在 RabbitMQ 中,消息的确认机制分为两种:自动确认模式(automatic acknowledgment)和显式确认模式(explicit acknowledgment)。默认情况下,RabbitMQ 使用自动确认模式,也称为自动 ACK 模式。在自动确认模式下,一旦消息被 RabbitMQ 投递给消费者,它就会立即将消息标记为已经被消费。即使消费者在处理消息时发生错误或者中断,消息也会被视为已经消费,并从队列中删除。这种情况下,消息只能被消费一次。
如果你使用显式确认模式,也称为手动 ACK 模式,在消费者处理完消息之后,你需要明确地向 RabbitMQ 确认消息已经被消费。只有在消息被显式确认之后,RabbitMQ 才会将消息标记为已被消费,并从队列中删除。在这种情况下,如果消费者处理消息时发生错误或者中断,消息不会被确认,它将会再次被投递给其他消费者,或者重新发送给当前的消费者,直到被成功处理并确认。
因此,如果你希望消息可以被多次消费,可以使用显式确认模式,并确保在消费者处理消息后正确地进行消息确认。
import com.rabbitmq.client.*;
public class RabbitMQConsumer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // RabbitMQ服务器地址
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 创建消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws java.io.IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
// 手动确认消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 开始消费消息,并关闭自动确认模式
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
在这个例子中,我们使用 channel.basicAck(envelope.getDeliveryTag(), false); 来手动确认消息。basicAck 方法用于向 RabbitMQ 服务器确认已经收到消息,并处理完消息。它的第一个参数是消息的投递标记(delivery tag),用来唯一标识一个消息。第二个参数是一个布尔值,用来表示是否确认多个消息。如果设置为 true,则会确认所有小于等于指定投递标记的所有消息。在大多数情况下,我们将其设置为 false,仅确认当前的消息。
另外,我们还通过 channel.basicConsume(QUEUE_NAME, false, consumer); 将消费者的自动确认模式设置为 false,即关闭了自动确认模式。这样一来,在处理完消息之前,消费者不会自动确认收到的消息。只有当消息被显式确认之后,RabbitMQ 才会将消息标记为已被消费。