Introduction
RabbitMQ是一个消息代理,其思想很简单,就是接收并转发消息。其实它的本质跟生活中的邮局的功能是一样的,就是接受个人的信件,并交到合适的人的手中。但它跟邮局的不同的是它处理的不是纸质的信件,而是二进制数据块—消息。
RabbitMQ中的术语:
Hello World!
Python Version (using the pika 0.9.8 Python client)
这个“Hello world”程序不会太复杂,仅仅完成发送一条消息,接收一条消息并将它输出到屏幕而已。为了完成这些功能,我们需要写两个程序:一个负责发送一条消息,一个负责接收信息并打印出来。
程序的总体设计如下:
生产者发送消息到hello队列,消费者从hello队列接收信息,并打印。
RabbitMQ库 RabbitMQ使用AMQP协议,因此我们需要一个AMQP协议库来与Rabbit通信。对于各种不同的语言会有不同的库可以选择。对于python来说,它也一样有amqp库,3个:py-amqplib、txAMQP、pika。 在这个例子中,我们采用pika 0.9.8。 下面我们将说明如何安装pika。 Linux环境下使用pip包管理工具: $ sudo pip installpika==0.9.8 Pika的安装依赖pip和git-core包,因此需要先安装它们: 1)Ubuntu: · $sudo apt-get install python-pip git-core 2)Debian: · $sudo apt-get install python-setuptools git-core · $sudo easy_install pip
|
Sending
第一个程序sengding.py将会向队列发出一条消息。
首先,我们需要向rabbitmq服务器建立一条连接:
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'))
channel = connection.channel()
这里,我们连接的是本地主机的rabbitmq服务器,因此使用localhost参数。如果rabbitmq服务器不在本地主机则输入相应的ip地址即可。
然后,在发送之前必须确保接收队列的存在。如果我们向一个不存在的队列发送消息,那么rabbitmq会将消息直接扔掉。因此,我们需要创建一个队列hello:
channel.queue_declare(queue='hello')
在创建了hello队列之后,我们就可以向hello队列发送消息了。一般来说,在rabbitmq中,一条消息不能直接被发送到队列,总是需要经过exchange来发送。但是在这里,我们使用默认的exchange来发送。默认的exchange是一个特殊的exchange,它允许我们使用参数routing_key精确的说明消息应该被送到哪个队列。
channel.basic_publish(exchange='',
routing_key='hello',
body='Hello World!')
print" [x] Sent 'Hello World!'"
在退出程序之前我们必须确保网络缓存被刷新,消息已经被传送到rabbitmq。因此我们可以使用关闭连接来达到此目的:
connection.close()
发送不能正常工作 如果你是第一次使用RabbitMQ且你没有看到“Sent”消息,然后你可能想知道是什么出错了。这种情况有可能是因为RabbitMQ已没有足够的可用磁盘空间(默认要求可用空间为1G),所以它会拒绝接收消息。可以检查rabbitmq的日志文件来确认问题并降低磁盘空间限制。可以参考配置文档来设置disk_free_limit参数。 |
Receiving
第二个程序receiving.py将负责接收hello队列的消息,并打印出来。
首先,程序需要连接到RabbitMQ服务器。代码跟sending.py的连接部分是一样的:
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'))
channel = connection.channel()
然后要确保队列存在。使用queue_declare创建队列是一个幂等操作,即无论我们跑多少次同样的代码都只会创建一个队列。
channel.queue_declare(queue='hello')
我们在receiving.py再次创建队列是因为可以确保队列是一定存在的。如果receiving程序先跑且不使用queue_declare创建队列,那么我们没有办法访问到hello队列,因为其不存在。
列出队列: 如果你需要查看rabbitmq里有什么队列可以使用如下命令: $ sudo rabbitmqctl list_queues Listing queues ... hello 0 ...done. 如果在windows平台下可以忽略sudo |
接收队列的消息比发送消息给队列复杂很多。这个过程是通过给队列订阅一个回调函数来完成的。只要我们接收信息,这个回调函数就会由Pika库执行。在这个案例中,这个函数将会用于将消息打印到屏幕上:
defcallback(ch,method,properties,body):
print " [x] Received %r" % (body,)
然后我们需要告诉RabbitMQ该回调函数将会接收hello队列的消息:
channel.basic_consume(callback,
queue='hello',
no_ack=True)
为了打印任务后,我们需要确保我们订阅的队列要存在,幸运的是我们可以使用queue_declare来确保队列存在。
最终,我们的程序进入一个死循环来等待数据并在必要的时候运行回调函数。
print' [*] Waiting for messages. To exit press CTRL+C'
channel.start_consuming()
Putting it all together
1) send.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/env python import pika
connection=pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel=connection.channel()
channel.queue_declare(queue='hello')
channel.basic_publish(exchange='', routing_key='hello', body='Hello World!') print" [x] Sent 'Hello World!'" connection.close() |
2) receive.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/env python import pika
connection=pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel=connection.channel()
channel.queue_declare(queue='hello')
print' [*] Waiting for messages. To exit press CTRL+C'
defcallback(ch,method,properties, body): print " [x] Received %r"% (body,)
channel.basic_consume(callback, queue='hello', no_ack=True)
channel.start_consuming() |
Running Result
1) 运行send.py
$ python send.py
[x] Sent 'Hello World!'
2) 运行receive.py
$ python receive.py
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World!'
Java Version (using the Java Client)
具体设计模型与pythonversion是一样的。
Java客户端库安装 首先下载客户端库包(http://www.rabbitmq.com/java-client.html),然后检查其签名是否正确。然后解压缩到你的工作目录,并提取jar文件。 $ unzip rabbitmq-java-client-bin-*.zip $ cp rabbitmq-java-client-bin-*/*.jar ./
|
安装完java客户端库之后我们就可以编写代码了。
Sending
我们这里称消息发送器为Send,消息接收器为Recv。发送器将会连接到RabbitMQ然后发送一条消息后退出。
在send.java文件中,我们需要import如下类:
importcom.rabbitmq.client.ConnectionFactory;
importcom.rabbitmq.client.Connection;
importcom.rabbitmq.client.Channel;
建立类和命令队列:
public class Send {
private final static StringQUEUE_NAME = "hello";
public static voidmain(String[]argv) throws java.io.IOException {
}
}
然后创建到服务器的连接:
ConnectionFactoryfactory = newConnectionFactory();
factory.setHost("localhost");
Connectionconnection= factory.newConnection();
Channelchannel = connection.createChannel();
上面的连接是基于socket来进行的。然后我们声明hello消息队列并发送信息:
queueDeclare(QUEUE_NAME, false, false, false, null);
Stringmessage = "HelloWorld!";
channel.basicPublish("",QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent'" +message + "'");
最后我们关闭channel和连接:
channel.close();
connection.close();
发送不能正常工作 如果你是第一次使用RabbitMQ且你没有看到“Sent”消息,然后你可能想知道是什么出错了。这种情况有可能是因为RabbitMQ已没有足够的可用磁盘空间(默认要求可用空间为1G),所以它会拒绝接收消息。可以检查rabbitmq的日志文件来确认问题并降低磁盘空间限制。可以参考配置文档来设置disk_free_limit参数。 |
Receiving
具体设计模型与pythonversion是一样的。
在receive.java文件中,我们需要插入如下类:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
额外的QueueingConsumer 是一个用户缓冲由服务器推送给我们的消息的类。
与sender一致,我们需要打开一个连接和channel,并声明与send一致的队列。
public class Recv {
private finalstatic StringQUEUE_NAME = "hello";
public staticvoidmain(String[]argv)
throwsjava.io.IOException,
java.lang.InterruptedException {
ConnectionFactoryfactory =new ConnectionFactory();
factory.setHost("localhost");
Connectionconnection =factory.newConnection();
Channelchannel =connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false,false, false, null);
System.out.println("[*] Waiting for messages. To exit press CTRL+C");
...
}
}
此外,我们需要告诉服务器从队列推送消息给我们。因为消息的推送是异步的,因此我们以一个缓存消息的对象的形式提供一个回调。那就是QueuingConsumer所做的事情:
QueueingConsumerconsumer =new QueueingConsumer(channel);
channel.basicConsume(QUEUE_NAME, true,consumer);
while (true){
QueueingConsumer.Deliverydelivery =consumer.nextDelivery();
Stringmessage =new String(delivery.getBody());
System.out.println("[x] Received '" + message + "'");
}
QueueingConsumer.nextDelivery()会阻塞程序,知道下一条消息的到达。
Puttingit all together
我们可以在classpath下同时编译send和recv程序:
$ javac -cp rabbitmq-client.jar Send.javaRecv.java
在运行他们的时候我们需要rabbitmq-client.jar以及它所以来的classpath:
$ 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
提示: 你可以将那些依赖保存到环境变量中: $exportCP=.:commons-io-1.2.jar:commons-cli-1.1.jar:rabbitmq-client.jar $java -cp$CPSend 在windows下: >setCP=.;commons-io-1.2.jar;commons-cli-1.1.jar;rabbitmq-client.jar >java -cp%CP%Send
|
参考:http://www.rabbitmq.com/tutorials/tutorial-one-python.html