RabbitMQ-官方文档翻译-1
介绍
RabbitMQ是一个消息代理:它接受并转发消息。你可以把它想象成一个邮局:当你把你想寄出的邮件放在一个邮箱里时,你可以确定送信人最终会把邮件寄给你的收件人。在这个类比中,RabbitMQ是一个邮箱、一个邮局和一个信件载体。
RabbitMQ和邮局之间的主要区别在于,它不处理纸张,而是接受、存储和转发二进制数据块消息。
RabbitMQ和消息传递一般使用一些术语:
- 生产只意味着发送。发送消息的程序是生产者:
- 队列是位于RabbitMQ内的邮箱的名称。尽管消息流经RabbitMQ和您的应用程序,但它们只能存储在队列中。队列只受主机内存和磁盘限制的限制,它本质上是一个大的消息缓冲区。许多生产者可以向一个队列发送消息,许多消费者可以尝试从一个队列接收数据。这就是我们表示队列的方式:
- 消费与接受有着相似的含义。消费者是一个主要等待接收消息的程序:
请注意,生产者、消费者和代理不必驻留在同一主机上;事实上,在大多数应用程序中,它们都没有。应用程序也可以是生产者和消费者。
动手写一个“Hello World”
在本教程的这一部分中,我们将用Java编写两个程序;发送单个消息的生产者和接收消息并将其打印出来的消费者。我们将略过JavaAPI中的一些细节,重点介绍这个非常简单的东西,以便开始。这是一个信息的“Hello World”。
在下图中,“P”是我们的生产者,“C”是我们的消费者。中间的框是一个队列——RabByMQ代表消费者的消息缓冲区。
Java客户端库RabbitMQ讲多种协议。本教程使用AMQP 0-9-1,这是一种用于消息传递的开放式通用协议。RabbitMQ有许多不同语言的客户端。我们将使用RabbitMQ提供的Java客户机。
下载客户端库及其依赖项(SLF4J API和SLF4J Simple)。复制工作目录中的这些文件以及教程Java文件。
请注意,SLF4J Simple对于教程来说已经足够了,但是您应该在生产中使用一个完整的日志库,比如Logback。
(RabbitMQ Java客户端也位于Maven中央存储库中,带有groupId com.RabbitMQ和artifactId amqp客户端。)
现在我们有了Java客户机及其依赖项,我们可以编写一些代码。
发送消息
在这里插入图片描述
我们将呼叫我们的消息发布者(发送者)Send和消息消费者(接收者)Recv。发布服务器将连接到RabbitMQ,发送一条消息,然后退出。
在Send.java中,我们需要导入一些类:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
设置类并命名队列:
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
...
}
}
然后我们可以创建到服务器的连接:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
}
该连接抽象了套接字连接,并为我们负责协议版本协商和身份验证等。在这里,我们连接到本地机器上的RabbitMQ节点,因此连接到本地主机。如果我们想连接到另一台机器上的节点,我们只需在这里指定它的主机名或IP地址。
接下来,我们创建一个通道,这是完成任务的大多数API所在的地方。注意,我们可以使用try with resources语句,因为连接和通道都实现java.io.Closeable。这样,我们就不需要在代码中显式地关闭它们。
要发送,我们必须声明要发送到的队列;然后我们可以将消息发布到队列,所有这些都在try with resources语句中:
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
声明队列是幂等的-仅当它不存在时才会创建它。消息内容是一个字节数组,因此您可以在那里编码任何您喜欢的内容。
发送不起作用!
如果这是您第一次使用RabbitMQ,并且您没有看到“已发送”消息,那么您可能会挠头想知道可能出了什么问题。可能代理启动时没有足够的可用磁盘空间(默认情况下至少需要200 MB的可用空间),因此拒绝接受消息。检查代理日志文件以确认并在必要时降低限额。配置文件文档将向您展示如何设置磁盘空闲限制。
接受消息
对我们的发布者来说就是这样。我们的使用者侦听来自RabbitMQ的消息,因此与发布单个消息的发布者不同,我们将保持使用者运行以侦听消息并将其打印出来。
代码(在Recv.java中)的导入与Send几乎相同:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
我们将使用额外的DeliverCallback接口来缓冲服务器推送给我们的消息。
设置与发布者相同;我们打开一个连接和一个通道,并声明要使用的队列。注意,这与发送发布到的队列相匹配。
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
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");
}
}
请注意,我们在这里也声明了队列。因为我们可能会在发布服务器之前启动使用者,所以我们希望在尝试使用来自该队列的消息之前确保该队列存在。
为什么不使用try with resource语句自动关闭通道和连接?通过这样做,我们只需让程序继续,关闭所有内容,然后退出!这会很尴尬,因为我们希望在使用者异步侦听消息到达时,进程保持活动状态。
我们将告诉服务器将队列中的消息传递给我们。由于它将异步推送消息,因此我们以对象的形式提供回调,该回调将缓冲消息,直到我们准备好使用它们为止。这就是DeliverCallback子类所做的。
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });