英文文档:http://www.rabbitmq.com/tutorials/tutorial-one-java.html
一、简介
RabbitMQ是一个消息代理,它可以接收和发送消息。你可以把它想象成一个邮局,当你把你想要寄的邮件放到一个邮箱里,你确信邮递员先生或者女士最终都会把你的邮件送到你的接收人那里。在这个比喻中,RabbitMQ是一个邮箱、邮局、邮递员。
RabbitMQ和邮局的主要区别是,RabbitMQ不会处理邮件(paper,其实是说RabbitMQ不会对消息进行处理,而邮局的话可能要盖章啥的)
它仅仅是接收、存储、发送二进制数据块--也就是消息
RabbitMQ传送消息,使用一些术语。(P生产者、Q队列、C消费者)
Producing意味着发送,一个发送消息的程序就是一个生产者。 P
队列是为一个邮箱取的名字,存在RabbitMQ内部中。尽管消息流经RabbitMQ和你的应用,但是它只能存储在队列中。队列只受主机内存和硬盘的限制,它本质上是一个很大的消息缓存。多个生产者可以往一个队列中发送消息,多个消费者也可以从一个队列中接收数据,这就是我们表示的队列 。 Q
Comsuming和接s收的含义类似,消费者是一个等待接受数据的程序。C
注意:生产者、消费者、代理不一定要放在同一台主机上,事实上在大部分情况下,他们不会在同一台主机(我现在是学习,所以我的都在同一台主机上)。
“Hello World”
“使用Java客户端”
这一部分教程中,我们将会写两个java程序;生产者用于发送简单的消息,消费者用于接收消息并把它们打印出来。我们将会详细介绍java API的细节,关注于简单的部分,用于开始。这是个"Hello world" 信息。
在下面的图中,P代表生产者,C代表消费者,中间的箱子是队列--RabbitMQ代表消费者保存的消息缓冲区
Java客户端库
RabbitMQ提供多种协议,这个教程使用开源的通用的消息协议AMQP 0-9-1。RabbitMQ有大量不同语言的客户端
我们将使用RabbitMQ提供的java客户端。
下载客户端库及其依赖(SLF4J API和SLF4J Simple)。 将这些文件复制到工作目录下,并沿着教程复制Java文件。
请注意SLF4J Simple对于教程来说已经足够了,但是在生产中你应该使用全面的日志库,比如Longback。
(RabbitMQjava客户端也在Maven中心库中,groupId com.rabbitmq和artifactId amqp-client)
现在我们有java客户端和它的依赖,我们可以写些代码。(有个鸡儿啊,为了方便,我这里使用Maven的管理方式)
发送:
我们将会称我们的消息发布者(发送者) Send ,我们的消息消费者 Recv ;发布者将和Rabbit连接,发送简单的消息,然后退出。
在Send.java中我们需要Import一些类
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
设置class命名队列。
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");//设置host
Connection connection = factory.newConnection();//获取连接
Channel channel = connection.createChannel();//获取channel
连接抽象socket连接,为我们负责协议版本协商和权限验证等等,这里我们在本地机器(本地主机)连接代理服务器,如果我们想要连接不同机器的代理服务器,我们只需要简单地指定域名或者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和连接。
channel.close();
connection.close();
发送不起作用
如果这是你第一次使用RabbitMQ,并且你没有看到"Sent"消息,你可能在绞尽脑计想哪里可能错了。或许是开启的代理服务不够足够的磁盘的空间(默认需要至少200M)所以拒绝接收消息。如果必要,检查代理服务的日志文件确认和减少限制。配置文档将教你如何设置disk_free_limit
接收
上述是我们的发布者。消费者从RabbitMQ中推送消息,所以不像发布者发布简单的消息那样,我们会保持它一直运行监听和打印消息
代码(Recv.java)和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接口的类,我们将用来缓存服务器推送的消息。
和发布者的设置一样,我们打开一个连接和channel,定义一个将要消费的队列,注意这将和发送的队列匹配(就是队列名要一样)。
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");
...
}
}
注意在这里声明一个队列,因为我们在发布者之前开始消费者监听,在我们消费消息之前我们想要确定队列的存在。
我们将告诉服务器将队列中的消息传送给我们。服务器将异步推送消息给我们,我们以对象的形式提供回调,它将缓冲消息直到我们准备好使用它们。这是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);
你可以在类路径下编译RabbitMQ java 客户端
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
在windows下,使用分号而不是冒号来分隔项目
消费者将打印从发布者从RabbitMQ获取的消息。消费者将保持运行,等待接受信息(使用Ctrl-C来停用),所以尝试在另外一个终端运行发布者。
你可能希望看到RabbitMQ有哪些队列和有多少信息在里面。你可以使用rabbitmqctl工具(使用特权用户)
sudo rabbitmqctl list_queues
在windows,使用sudo
rabbitmqctl.bat 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
或者在windows下:
set CP=.;amqp-client-4.0.2.jar;slf4j-api-1.7.21.jar;slf4j-simple-1.7.22.jar
java -cp %CP% Send
(以下不是翻译内容)
上述的过程可能比较繁琐,下面我是用eclipse直接使用:代码还是使用上述代码,用maven的方式管理:
创建一个maven工程,在pom.xml中引入依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
发布者:
package com.lizhi;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
public class Send {
private static final 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);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
消费者:
package com.lizhi;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException, java.lang.InterruptedException, 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");
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);
}
}
然后先运行消费者,处于监听状态,然后再运行发布者,结果如下:
消费者(刚开始处于监听状态):
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[*] Waiting for messages. To exit press CTRL+C
发布者:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[x] Sent 'Hello World!'
接收到的消息:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World!'