RabbitMQ是一个消息代理,即它的功能为:接受和转发消息。
你可以把它想象成一个邮局:当你把你想要的邮件放在邮箱里,你可以肯定邮差先生最终会把邮件送到你的收件人那里。在这个类比中,RabbitMQ相当于一个邮筒、一个邮局和一个邮差。
RabbitMQ与邮局的主要区别在于,它不处理纸张,而是接受、存储和转发数据的二进制数据。
RabbitMQ和一般的消息传递使用到一些术语:
生产( producing)意味着发送消息。发送消息的程序是生产者( producer ):
队列( queue)是在RabbitMQ内的一个邮箱的名称。尽管消息通过RabbitMQ和您的应用程序流,但它们只能存储在队列中。队列仅由主机的内存和磁盘限制绑定,它本质上是一个大的消息缓冲(buffer)。许多生产者可以发送到一个队列的消息,许多用户可以尝试从一个队列接收数据。这就是我们如何表示一个队列:
消费( consuming)也有类似的含义。消费者( consumer)是一个主要等待接收消息的程序:
请注意,生产者、使用者和代理(broker)不必驻留在同一主机( host )上 。实际上在大多数应用程序中,它们不需要驻留在同一主机上。
在本教程中,我们将在Java中编写两个程序 : 一个发送单个消息的生成器,以及接收
消息并将其打印出来的使用者。我们将对Java API中的一些细节进行粉饰,将注意力集中在这个非常简单的事情上。这只是一个“Hello World”的消息传递。
在下面的简化图中,“P”是我们的生产者,“C”是我们的消费者。
中间的框是一个队列——RabbitMQ代表消费者保存的消息缓冲区。
这里需要顺便提一下Java客户端库( the Java cilent library ):
RabbitMQ讲多种协议。本教程使用AMQP 0 - 9 - 1,这是一种开放的、通用的消息传递协议。有许多不同语言的RabbitMQ客户。我们将使用RabbitMQ提供的Java客户机。下载客户端库(client library)及其依赖关系(SLF4J API 和SLF4J Simple)。将这些文件以及教程Java文件,复制到您的工作目录中。
请注意,SLF4J Simple就足够用于教程,但是您应该使用一个成熟的日志库,如在生产中的Logback。(带有标识符com.rabbitmq和名字 amqp - client的RabbitMQ Java客户机,也在中央Maven存储库中。)
现在有了Java客户端库及其依赖关系,我们可以开始编写代码。
我们将称呼我们的消息发布者(publisher)为 Send 和我们的消息接收者(或者消费者)为 Recv 。Send 将连接到RabbitMQ,发送一条消息,然后退出。
首先,在Send.java中,我们需要import一些类:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
创建Send类,给队列queue命名。
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException {
...
}
}
然后我们可以创建到服务器的连接。
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
这里我们连接到本地机器上的一个代理——本地主机(localhost)。如果我们想要连接到另一台机器上的代理,我们只需在这里指定它的名称或IP地址。然后,我们创建了一个channel,它是用于完成大部分任务的API。为了发送消息,我们必须声明一个queue供我们发送,然后我们就可将消息发布到queue:
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 + "'");
声明队列queue是幂等的——它只会在不存在的情况下创建。消息内容是一个byte数组,因此你可以以任何你喜欢的方式进行编码。
最后,我们关闭连接和channel:
channel.close();
connection.close();
这里是完整的Send.class:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
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");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
注意:如果这是你第一次使用RabbitMQ,而你没有看到“发送”消息。这可能是代理开始时没有足够的空闲磁盘空间(默认情况下,它至少需要200 MB),因此拒绝接受消息。检查代理日志文件以确认并减少必要的限制。配置文件文档将显示如何设置disk_free_limit。
消费者接收信息:
我们的消费者需要获取来自RabbitMQ的消息,所以不同于发布单一消息的发布者,我们将保持消费者运行以侦听消息并将其打印出来。
首先,同理,我们需要import一些类:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
上边的DefaultConsumer是一个实现Consumer接口的类,我们将使用它来缓冲服务器向我们推送的消息。
接下来的设置与 Send 相同。我们打开一个连接和一个channel,并声明我们将要使用的队列queue。
请注意,这里声明的queue与Send发布消息到的queue相匹配(声明的各参数相同)。
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException,
java.lang.InterruptedException {
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");
...
}
}
注意,这里也声明了queue(而不是直接调用)。因为我们可能在发布者之前启动消费者,所以我们应该保证在尝试使用它之前,确保queue存在。
我们将告诉服务器将消息从queue传递给我们。由于它将异步推送消息,所以我们提供一个对象的形式的回调,它将对消息进行缓冲,直到我们准备好使用它们为止。这就是DefaultConsumer类的作用。
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
这里是完整的Recv.java:
import com.rabbitmq.client.*;
import java.io.IOException;
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");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
将上述文件放置在同一目录下(包括上面提到的客户端库及其依赖关系):
你可以在类路径中仅使用RabbitMQ java client来编译这两个类:
javac -cp amqp-client-4.0.2.jar Send.java Recv.java
要运行它们,您需要rabbitmq-client.jar及其在类路径上的依赖关系。
然后在终端,运行消费者(接收器):
java -cp .:amqp-client-4.0.2.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.22.jar Recv
然后,运行生产者(发送器):
java -cp .:amqp-client-4.0.2.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.22.jar Send
最后,你可能希望看到RabbitMQ有哪些队列,以及其中有多少消息。可以使用rabbitmqctl工具(要有root权限)以获得相关信息:
sudo rabbitmqctl list_queues
为了简化每一次的编译执行命令,你可以为类路径设置一个环境变量
export CP=.:amqp-client-4.0.2.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.22.jar
//设置环境变量
java -cp $CP Send
//简化编译执行命令
rabbitmq-c++(SimpleAmqpClient) 笔记代码系列:
rabbitmq-c++(SimpleAmqpClient) 笔记代码一
rabbitmq-c++(SimpleAmqpClient) 笔记代码二
rabbitmq-c++(SimpleAmqpClient) 笔记代码三
rabbitmq-c++(SimpleAmqpClient) 笔记代码四
rabbitmq-c++(SimpleAmqpClient) 笔记代码五
rabbitmq-c++(SimpleAmqpClient) 笔记代码六
RabbitMQ 学习笔记系列:
RabbitMQ 学习笔记(一):简单介绍及”Hello World”
RabbitMQ 学习笔记(二):work queues
RabbitMQ 学习笔记(三):Publish/Subscribe
RabbitMQ 学习笔记(四):Routing
RabbitMQ 学习笔记(五):Topics
RabbitMQ 学习笔记(六):RPC