RabbitMQ 学习笔记(一):简单介绍及"Hello World"

RabbitMQ 简单介绍:

RabbitMQ是一个消息代理,即它的功能为:接受和转发消息

你可以把它想象成一个邮局:当你把你想要的邮件放在邮箱里,你可以肯定邮差先生最终会把邮件送到你的收件人那里。在这个类比中,RabbitMQ相当于一个邮筒、一个邮局和一个邮差。

RabbitMQ与邮局的主要区别在于,它不处理纸张,而是接受、存储和转发数据的二进制数据。

RabbitMQ和一般的消息传递使用到一些术语:

生产( producing)意味着发送消息。发送消息的程序是生产者( producer ):

队列( queue)是在RabbitMQ内的一个邮箱的名称。尽管消息通过RabbitMQ和您的应用程序流,但它们只能存储在队列中。队列仅由主机的内存和磁盘限制绑定,它本质上是一个大的消息缓冲(buffer)。许多生产者可以发送到一个队列的消息,许多用户可以尝试从一个队列接收数据。这就是我们如何表示一个队列:

消费( consuming)也有类似的含义。消费者( consumer)是一个主要等待接收消息的程序:

请注意,生产者、使用者和代理(broker)不必驻留在同一主机( host )上 。实际上在大多数应用程序中,它们不需要驻留在同一主机上。

简单的应用 — “Hello World!”

在本教程中,我们将在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 和我们的消息接收者(或者消费者)为 RecvSend 将连接到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

你可能感兴趣的:(RabbitMQ)