RabbitMQ官方教程一 "Hello World!"

RabbitMQ官方教程一 "Hello World!"

本文译自RabbitMQ Tutorials One,实例使用Java。

介绍

RabbitMQ是一个消息代理。它的核心思想非常简单:接收并转发消息。你可以把它想象成一个邮局:当你把邮件丢进邮箱时,你非常确定邮递员先生会把它送到收件人手中。在这个比喻中,RabbitMQ就是邮箱、邮局和邮递员。

RabbitMQ和邮局的主要区别是它处理的不是纸张。它接收、存储并转发二进制数据块,也就是message,消息。

通常来讲,RabbitMQ和消息传递中会用到一些术语:

    • producing的意思是发送。一个发送消息的程序叫做producer。图中我们用一个“P”来表示它:
    • 一个queue,即队列,相当于一个邮箱,它由RabbitMQ管理。尽管消息会在你的应用和RabbitMQ之间流过,但他们只被保存在队列中。队列没有边界限制,你想存多少消息就能存多少——它本质上是一个无限制的缓冲区。一个队列可以接收多个producer的消息,也可以被多个consumer读取。下图是一个队列,上边的字代表它的名字:
    • consuming的意思类似于接收。一个等待接收消息的程序叫做consumer。在图中我们用一个“C”来表示它。

要注意,producer、consumer和消息代理不需要生活在同一台机器上,事实上大多数应用中它们会分开住。

“Hello World”

采用Java客户端

在这部分教程中,我们会写两个Java程序。一个发送一条消息的producer和一个接收消息并打印出来的consumer。因为只是刚刚起步,我们会忽略一些Java API的细节,只把精力集中在简单的事情上。消息的内容是“Hello World”。

下图中,“P”是我们的producer,“C”是我们的consumer。而中间的方形盒子是我们的队列——RabbitMQ代表消费者保留的消息缓冲区(原文为: a message buffer that RabbitMQ keeps on behalf of the consumer. )。

Java客户端库

RabbitMQ可以使用多种协议。这篇教程使用的是AMQP 0-9-1,一个开放、通用的消息协议。对于不同的语言,RabbitMQ有许多不同的客户端。这里我们使用RabbitMQ提供的Java客户端。
下载客户端压缩包,按照描述检查它的signature。解压到你的工作目录下,并从中取出所有JAR文件。
$ unzip rabbitmq-java-client-bin-*.zip
$ cp rabbitmq-java-client-bin-*/*.jar ./
RabbitMQ的java客户端可以通过maven下载,它的groupId是com.rabbitmq,artifactId是amqp-client。

现在我们有了Java客户端和它的依赖包,可以动手写代码了。

发送


我们把发消息的类叫做Send,收消息的类叫做Recv。Send类将会连接(connect)RabbitMQ,发送一条消息然后退出。

在Send.java中,我们需要import一些类:

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;

创建类并且为queue起个名字:

public class Send {
  private final static String QUEUE_NAME = "hello";

  public static void main(String[] argv)
      throws java.io.IOException {
      ...
  }
}

然后我们创建一个到server的connection:

    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

connection是socket连接的抽象,并且为我们管理协议版本协商(protocol version negotiation),认证(authentication )等等事情。这里我们要连接的消息代理在本地,因此我们将host设为“localhost”。如果我们想连接其他机器上的代理,只需要将这里改为特定的主机名或IP地址。

接下来,我们创建一个channel,绝大部分API方法需要通过调用它来完成。

发送之前,我们必须声明消息要发往哪个队列,然后我们可以向队列发一条消息:

    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 + "'");

队列的声明是幂等的,也就是说只有当队列不存在时才会创建它。消息内容是byte数组,因此你可以使用各种编码方式。

最后,我们把channel和connection关掉:

    channel.close();
    connection.close();

这里是完整的Send.java类

消息没发出去!

如果这是你第一次使用RabbitMQ,并且你没有看到“Sent”信息被打印出来,那你可能会在抓耳挠腮,搞不清哪里出了错。也许消息代理启动时没有足够的磁盘空间(默认需要1Gb)所以它拒绝了消息。检查一下代理的logfile,假如有必要的话也可以减少磁盘空间的限制。配置文件文档将会告诉你如何设置disk_free_limit。

接收

以上就是我们的发送者。RabbitMQ会把消息推送给接收者,所以不同于只发了一条信息的发送者,我们会让接收者一直监听消息并打印出来。

Recv.java的import部分和Send类差不多:

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接口的实现,我们使用它来接收server推送来的消息。
起始的代码和sender差不多(译注:都是样板代码):我们创建连接,打开channel,并且声明我们要监听的队列。注意这个队列要与Send类要发送的队列一致。

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");
    ...
    }
}
注意到,我们在这里也声明了队列。因为我们可能在启动发送者之前启动接收者,因此需要保证在接收消息之前,队列已经存在。

接下来我们要告诉server把队列中的消息发送给我们。因为推送消息是异步的,我们需要以对象的形式提供一个回调,它会缓存消息,直到我们准备好使用它。我们通过一个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类

合到一起

原文中使用命令行编译执行代码,略显繁琐,此处选择不翻译。在IDE中只需运行这两个类即可。完整信息请参照原文中Putting it all together部分。

发送者会通过RabbitMQ发送一条信息,而接收者会把它打印出来。接收者将会一直运行,等待接收消息。

如果想要检查队列,可以使用rabbitmqctl list_queues。

# rabbitmqctl list_queues
Listing queues ...
hello        0

现在我们可以前往第二部分(链接为原文),构建一个简单的工作队列(work queue)。

编辑于 2016-12-14