1 Hello World!


官方文档地址: 1 “Hello World!”


最简单的东西。

前提条件

本教程假设你已经安装了 RabbitMQ 并在本地主机端口(5672)上运行。

简介

RabbitMQ 是一个消息代理,负责接收和转发消息。你可以把它想象成一个邮局:当你把想要投寄的邮件放入一个邮箱时,你可以确定邮递员最终会把邮件送到你指定的收件人那里。在这个类比中,RabbitMQ 是一个邮箱,也是一个邮局和一个邮递员。

RabbitMQ 和邮局之间的主要区别是它不处理纸张,而是接受、存储和转发数据消息的二进制大对象。

RabbitMQ 和一般的消息传递都使用了一些术语。

  • 生产无非是为了发送。发送消息的程序是producer
    在这里插入图片描述
  • queue是驻留在 RabbitMQ 内的邮箱的名称。一个队列只受主机的内存和磁盘限制,它本质上是一个大的消息缓冲区。许多生产者可以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。我们是这样表示队列的:
    在这里插入图片描述
  • 消费和接收有类似的含义。consumer是一个等待接收消息的程序:
    在这里插入图片描述

Hello World

在本部分教程中,我们将用Java编写两个程序:发送消息的生产者和接收消息并将其打印出来的消费者。我们将使用消息队列传递"Hello World"作为例子进行讲解。

在下面的图表中,"P"是我们的生产者,"C"是我们的消费者。中间的盒子是一个队列。
在这里插入图片描述

Java客户端库

RabbitMQ 使用多个协议。本教程使用AMQP 0-9-1,这是一种开放的、通用的消息传递协议。RabbitMQ 有许多不同语言的客户端。我们将使用 RabbitMQ 提供的 Java 客户端。

下载客户端库及其依赖项(SLF4J APISLF4J Simple)。将这些文件以及教程中的 Java 文件复制到您的工作目录中。

请注意,对于教程来说,SLF4J Simple已经足够了,但是您应该在生产环境中使用完整的日志库,比如Logback

(中央 Maven 库中也有 RabbitMQ 的 Java 客户端,groupId 为com.rabbitmq,artifactId 为amqp-client)。

现在我们有了 Java 客户端及其依赖项,可以编写一些代码了。

这里补充下 RabbitMQ 的 Java 客户端的 Maven 依赖,方便 Maven 项目使用:

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.7.1</version>
</dependency>

生产者

在这里插入图片描述
我们将调用消息发布者(发送方)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 节点 - 也就是localhost。如果我们想连接到另一台机器上的节点,我们只需在这里指定它的主机名或IP地址。

接下来我们创建一个channel,它是完成任务的大部分 API 所在的地方。注意,我们可以使用try-with-resources语句,因为ConnectionChannel都实现了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 + "'");

声明队列是幂等的 - 只有在队列不存在时才会创建它。消息内容是一个字节数组,因此您可以对其中的任何内容进行编码。

这是完整的Send.java代码:

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author wangbo
 * @date 2019/10/22 18:05
 */
public class Send {
    
    
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
    
    
        //创建一个连接器连接到服务器
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try(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 + "'");
        }
    }
}

生产者不工作

如果这是你第一次使用 RabbitMQ,代码执行了但是没有看到“发送”的消息,原因可能是服务启动时没有足够的空闲磁盘空间(默认情况下,它需要至少200MB的空闲空间),因此拒绝接收消息。检查服务器日志文件以确认问题并在必要时减少限制。配置文件文档将向你展示如何设置disk_free_limit

消费者

我们的消费者监听来自 RabbitMQ 的消息,所以不像发布者只发布一条消息就可以退出了,我们需要保持消费者运行来监听消息并打印出来。
在这里插入图片描述
Recv.java需要导入的类和Send.java大体一样:

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 -> {
    
     });

这是完整的Recv.java代码:

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author wangbo
 * @date 2019/10/22 18:25
 */
public class Recv {
    
    
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
    
    
        //创建一个连接器连接到服务器
        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");
        //回调对象
        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 -> {
    
    });
    }
}

把它们放一起

你可以使用 RabbitMQ 的 Java 客户端在类路径中编译这两个:

javac -cp amqp-client-5.7.1.jar Send.java Recv.java

要运行它们,您需要在类路径上添加rabbitmq-client.jar及其依赖。在终端中,运行消费者(接收方):

java -cp .:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar Recv

然后,运行生产者(发送方):

java -cp .:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar Send

在 Windows 上,使用分号而不是冒号来分隔类路径中的项。

消费者将打印它通过 RabbitMQ 从生产者获得的消息。消费者将继续运行,等待消息(可以使用Ctrl+C来停止它),所以需要从另一个终端运行生产者。

队列列表

您可能想要查看 RabbitMQ 有哪些队列以及队列中有多少消息。你可以(以管理员权限)使用rabbitmqctl工具:

sudo rabbitmqctl list_queues

在 Windows 上:

rabbitmqctl.bat list_queues

接下来是第2部分,构建一个简单的工作队列。

提示

为了节省输入,你可以为类路径设置一个环境变量。

export CP=.:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar
java -cp $CP Send

在 Windows 上:

set CP=.;amqp-client-5.7.1.jar;slf4j-api-1.7.26.jar;slf4j-simple-1.7.26.jar
java -cp %CP% Send

实际测试

下面的并非官方教程的内容,是我在本地 Windows 系统上对官方教程的测试。

下载三个 Jar 包,放到了目录D:\test。将Send.javaRecv.java也放到该目录下。执行编译命令:

D:\test>javac -cp amqp-client-5.7.1.jar Send.java Recv.java

在这里插入图片描述
启动消费者,运行Recv.class

D:\test>java -cp .;amqp-client-5.7.1.jar;slf4j-api-1.7.26.jar;slf4j-simple-1.7.26.jar Recv
 [*] Waiting for messages. To exit press CTRL+C

在新窗口启动生产者,运行Send.class

D:\test>java -cp .;amqp-client-5.7.1.jar;slf4j-api-1.7.26.jar;slf4j-simple-1.7.26.jar Send
 [x] Sent 'Hello World!'

这时可以在消费者窗口接收到生产者发送的Hello World!字符串。

 [x] Received 'Hello World!'

再测试下设置环境变量,简化消息发送命令,如下所示,没有问题:

D:\test>set CP=.;amqp-client-5.7.1.jar;slf4j-api-1.7.26.jar;slf4j-simple-1.7.26.jar

D:\test>java -cp %CP% Send
 [x] Sent 'Hello World!'

D:\test>java -cp %CP% Send
 [x] Sent 'Hello World!'

D:\test>

接下来测试下rabbitmqctl工具,查看 RabbitMQ 有哪些队列以及队列中有多少消息。

D:\Programmer\RabbitMQ Server\rabbitmq_server-3.7.8\sbin>rabbitmqctl.bat list_queues
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
hello   0

可以看出有个队列,名字是hello,消息0条。

猜你喜欢

转载自blog.csdn.net/wb1046329430/article/details/115273118