本系列教程主要针对使用java语言进行Rabbitmq的相关编程。阅读前请确认已经安装过rabbit服务。关于如何安装rabbitmq,请参考如何使用rabbitmq.
介绍
RabbitMQ是一个消息代理中间件。主要概念就是:接收、转发消息。你也可以将它看成是一个邮局:当你把邮寄物品投递到邮箱时,你可以确认这件物品最终会到达你指定的收件人手中。你可以将RabbitMQ比喻成邮箱、邮局、邮差。
RabbitMQ 和邮局最主要区别就是,RabbitMQ不处理纸质信件,它的职责是接收、存储和发送二进制数据-消息。
一般来说,RabbitMQ 和消息机制有一些专业术语需要了解一下:
生产者 生产,意味着只需要发送消息。发送消息的程序就是生产者。我们用统一图样来表示生产者:
队列 类似于我们的邮箱。队列是在RabbitMQ进程中的。在一个队列里可以存储RabbitMQ和你的应用程序之间的消息数据流。队列理论上来说是不受任何限制的,它可以存放你想要存放任意数量的消息数据 —— 你可以认为它是一个无限的缓冲区。许多生产者可以发送消息到队列中,许多消费者也可以从队列中获取消息数据。我们也用统一图样来表示队列:
消费者 消费者好比收件人。等待接收消息的程序即可称之为消费者。我们也用统一图样来表示消费者:
注意:生产者、消费者和消息代理不一定在同一个机器上,实际场景中大都是在分布式集群环境中。
Hello World
使用Java 客户端
这部分教程中我们将会编写两个java程序。一个发送一条简单消息的生产者,和一个消费者接收消息并打印到控制台。我们不谈java API的一些细节,先把注意力集中在这个简单的入门程序中。
在下面的图例中,P 是我们的生产者,C 是我们的消费者。中间的盒子是一个队列 —— RabbitMQ用于为消费者缓存消息。
Java client library
RabbitMQ 允许使用多种协议. 本系列教程使用的是 AMQP 0-9-1, 是一个开放的通用的消息传递协议。 针对不同语言,有不同的RabbitMQ连接客户端。 本教程我们使用RabbitMQ提供的java客户端连接工具。
你可以下载java client lib.jar包,并校验一下签名。下载后解压缩jar包到你工作目录中:
$ unzip rabbitmq-java-client-bin-*.zip
$ cp rabbitmq-java-client-bin-*/*.jar ./
我们也可以使用如下Maven依赖引入:
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
现在我们已经有了java客户端连接的依赖了,可以开始写一写代码了。
发送消息
我们写一个消息发送的类Send、一个接收消息的类Recv.
在Send.java类中,需要引入以下几个类:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
设置类并命名队列名称为”hello”:
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException {
//do your job...
}
}
然后我们继续在main方法中添加连接到RabbitMQ的代码:
//创建连接工厂类
ConnectionFactory factory = new ConnectionFactory();
//设置需要连接到的RabbitMQ主机地址
factory.setHost("localhost");
//创建一个连接
Connection connection = factory.newConnection();
//创建一个信道
Channel channel = connection.createChannel();
Connection抽象了socket连接,并负责对我们的协议版本的协商和身份验证等。这里我们连接到了本地机器的一个代理服务器,如果我们要连接到其他机器,则只需调整一下ip地址即可。
然后我们创建了一个channel,它会提供大部分的API帮助我们在RabbitMQ中做想做的事情。
发送消息,我们需要声明一个该消息缓存的队列;声明过后,我们就可以把消息发送到队列中了。
//声明一个队列,队列名称为"hello"
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并且你也看不到发送的消息,可能会很困惑是哪里出了问题。这可能是RabbitMQ启动时赋予的空间不足(默认情况下是1Gb)导致服务器拒绝接受消息。这时你需要检查日志文件,可能的话还需要降低空间大小的限制。配置文件的文档教你如何设置disk_free_limit。
接收消息
消费者是从RabbitMQ接收消息,所以跟前面的生产者有点不同只是发送了一条简单的消息,消费者需要保持连接监听消息并输出它们。
Recv.java类同样需要导入一些重要的类:
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
接口的一个实现类,我们用它缓存服务端发送过来的消息。
消费者连接设置和生产者是一样的。打开一个连接、信道,声明一个将去消费的队列。注意:消费者这些设置参数应该和生产者是一样的。
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");
//do your job ...
}
}
注意:这里我们同样声明了一个名称和生产者一致的队列。因为我们可能在生产者发送消息前启动消费服务,我们需要确保在消费消息数据之前队列是存在的。
我们要告诉生产者通过队列将消息传给消费者。由于生产者发送消息是异步的,消费端提供了一个对象(该对象缓存消费端将要使用的消息)的回调。这就是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类的源码链接。
运行程序实例
可以导入maven工程到eclipse(idea),执行Send.java、Recv.java的main方法
step1:运行Recv的main方法,消费者等待队列的消息,在消费者一端的控制台输出如下:
[*] Waiting for messages. To exit press CTRL+C
step2:运行Send的main方法,生产者发送消息,在生产者一端的控制台输出如下消息后,并且关闭了服务连接:
[x] Sent 'Hello World!'
此时查看消费者一端,可以看到控制台增加了一条输出内容,正是从队列获取的消息:
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World!'
step3:这时候我再次启动一个服务(运行Send的main方法),又往队列发送一条Hello World!的消息:
[x] Sent 'Hello World!'
此时继续查看消费者一端,可以看到控制台又增加了一条输出内容,正是第二次从队列获取的消息:
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World!'
[x] Received 'Hello World!'
执行完程序后,你也可以进入web界面查看队列状态。关于更多RabbitMQ的web监控管理界面内容,可以参考启动rabbitmq web管理后台插件
使用javac -cp命令
编译源代码
javac -cp rabbitmq-core.jar Send.java Recv.java
运行前面编译后的字节码文件,命令行操作这里要求classpath添加了rabbitmq-client.jar的依赖
java -cp .:commons-io-1.2.jar:commons-cli-1.1.jar:rabbitmq-client.jar Send
java -cp .:commons-io-1.2.jar:commons-cli-1.1.jar:rabbitmq-client.jar Recv
windows环境下,使用分号符;替代:执行命令。
在终端执行时,消费者一直是运行着的,在等待着接收消息(仅限终端执行Ctrl-C 停止运行),所以你可以使用命令再运行一个生产者去发送一个新的消息。
可以在RabbitMQ的安装目录下sbin目录下使用rabbitmqctl list_queues命令查看队列的一些属性:
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5\sbin>rabbitmqctl list_queues
Listing queues ...
hello_queue 0
学完这节,是时候学习第二节内容,去创建一个工作队列了。
原文链接:http://www.rabbitmq.com/tutorials/tutorial-one-java.html