rabbitmq介绍

RabbitMQ is a message broker. The principal idea is pretty simple: it accepts and forwards messages. You can think about it as a post office: when you send mail to the post box you're pretty sure that Mr. Postman will eventually deliver the mail to your recipient. Using this metaphor RabbitMQ is a post box, a post office and a postman.(rabbitmq官网介绍)


Hello World

第一个例子是hello world,官网上rabbitmq的客户端装的是pika。
[root@pxe ~]# pip install pika
hello world模型最简单:

wKioL1VTWA_yUf0MAAAyyIKTNdI804.jpg


生产者
[root@pxe test]# cat rabbit_send.py
#!/usr/bin/env python
import pika
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost'))  # 创建一个连接
channel = conn.channel()                          # 创建一个channel
channel.queue_declare(queue='hello')     # 创建一个hello队列
channel.basic_publish(exchange='',routing_key='hello',body='hello world!')   # 往hello队列发送信息,routing_key跟具体队列名
print "Sent 'hello world"  
conn.close()     # 关闭连接

消费者
# 从队列中接收消息比较复杂,需要订阅一个callback函数到队列中去。
[root@pxe test]# cat rabbit_rev.py
import pika
conn = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = conn.channel()
channel.queue_declare(queue='hello')
print "waiting for message!"
def callback(ch, method, properties, body):
    print "receive %r" % body
channel.basic_consume(callback,queue='hello',no_ack=True)  # 从hello队列中接收消息
channel.start_consuming()   # 这步相当于启动一个进程一直从hello队列中获取消息

执行结果
[root@pxe test]# python rabbit_send.py  # 发送消息
Sent 'hello world'
[root@pxe test]# rabbitmqctl list_queues name messages_ready messages_unacknowledged   # 查看队列情况
Listing queues ...
hello	1	0
...done.

[root@pxe test]# python rabbit_rev.py  # 接收消息
waiting for message!
receive 'hello world!'


Work queues

第二个例子演示的是多个消费者轮询从队列中接收消息
模型如下:

wKiom1VYC7bTJgL0AABDrClTkXo372.jpg

生产者
[root@pxe test]# cat rabbit_task.py
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))   # 创建一个连接
channel = connection.channel()   # 创建一个channel
channel.queue_declare(queue='task_queue', durable=True)  # 宣告一个持久化队列,队列名为task_queue,durable为True的时候,rabbitmq重启队列也不会消失
message = ' '.join(sys.argv[1:]) or "Hello World!"       
channel.basic_publish(exchange='',routing_key='task_queue',body=message,properties=pika.BasicProperties(delivery_mode=2)) # delivery_mode=2使message存到disk上,但这并不是百分百保证消息不会丢
print "Sent %r" % message 
connection.close()

消费者
[root@pxe test]# cat rabbit_worker.py
#!/usr/bin/env python
import pika
import time
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))  # 创建一个连接
channel = connection.channel()   # 创建一个channel
channel.queue_declare(queue='task_queue', durable=True)  # 宣告一个持久化队列
print ' [*] Waiting for messages. To exit press CTRL+C'
def callback(ch, method, properties, body):              # 回调函数
    print "Received %r" % body
    time.sleep(body.count('.'))                    # 纯粹是为了演示效果
    print "Done"
    ch.basic_ack(delivery_tag = method.delivery_tag)    # no_ack不为True的情况下,消费者进程挂掉的时候,所有未确认的消息都会被重传
channel.basic_qos(prefetch_count=1)                     # 让不同的worker进程轮询接收消息
channel.basic_consume(callback,queue='task_queue')
channel.start_consuming()

执行结果
[root@pxe test]# python rabbit_task.py first.
Sent 'first.'
[root@pxe test]# python rabbit_task.py second..
Sent 'second..'
[root@pxe test]# python rabbit_task.py third...
Sent 'third...'
[root@pxe test]# python rabbit_task.py fourth....
Sent 'fourth....'
[root@pxe test]# python rabbit_task.py fifth.....
Sent 'fifth.....'

shell-1
[root@pxe1 test]# python rabbit_worker.py
 [*] Waiting for messages. To exit press CTRL+C
Received 'first.'
Done
Received 'third...'
Done
Received 'fifth.....'
Done

shell-2
[root@pxe2 test]# python rabbit_worker.py
 [*] Waiting for messages. To exit press CTRL+C
Received 'second..'
Done
Received 'fourth....'
Done


Topics

基于topic exchange没有唯一的routing_key,它可能是一些word的集合,通常这些word会表明它的feature.

wKiom1XyRZGDKKKLAABskRlXM1Y600.jpg

* 代表一个word

# 代表0或多个word


1、routing_key "quick.orange.rabbit"的消息会被发送到Q1、Q2

2、routing_key "quick.orange.fox" 的消息只会被发送到Q1

3、routing_key "lazy.brown.fox" 的消息只会被发送到Q2

4、routing_key "lazy.orange.male.rabbit" 的消息只会匹配最后一个word,会被发到Q2


# 下面是官方的一个例子
[root@controller001 home]# cat emit_log_topic.py
#!/usr/bin/env python
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='topic_logs',type='topic')

routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'

channel.basic_publish(exchange='topic_logs',routing_key=routing_key,body=message)
print "[x] Sent %r:%r" % (routing_key, message)
connection.close()


[root@controller001 home]# cat receive_logs_topic.py
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue

binding_keys = sys.argv[1:]
if not binding_keys:
   print >> sys.stderr, "Usage: %s [binding_key]..." % (sys.argv[0],)
   sys.exit(1)

for binding_key in binding_keys:
    channel.queue_bind(exchange='topic_logs',queue=queue_name,routing_key=binding_key)

print "[*] waiting for logs. To exit press CTRL+C"

def callback(ch, method, properties, body):
    print "[x] %r:%r" % (method.routing_key, body,)

channel.basic_consume(callback,queue=queue_name,no_ack=True)
channel.start_consuming()

运行结果
[root@controller001 home]# python receive_logs_topic.py "kern.*"
[*] waiting for logs. To exit press CTRL+C

[root@controller001 home]# python emit_log_topic.py "kern.critical" "A critical kernel error"
[x] Sent 'kern.critical':'A critical kernel error'


参考链接

rabbitmq tutorials

pika使用手册:http://pika.readthedocs.io/en/0.10.0/modules/parameters.html