RabbitMQ
RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。
RabbitMQ安装
安装配置epel源 # rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm 安装erlang # yum -y install erlang 安装RabbitMQ # yum -y install rabbitmq-server
启动/停止 service rabbitmq-server start/stop
安装API pika pip install pika easy_install pika
使用API操作RabbitMQ
import pika # ######################### 生产者 ######################### connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='MQ1') channel.basic_publish(exchange='',routing_key='MQ1',body='Hello World!') print(" [x] Sent 'Hello World!'") connection.close()
import pika # ########################## 消费者 ########################## connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='MQ1') def callback(ch, method, properties, body): print(" [x] Received %r" % body) channel.basic_consume(callback,queue='MQ1',no_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
1、ackonowlege 消息不不丢失
no-ack = False,如果生产者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。
import pika # ########################## 消费者 ########################## connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='MQ1') def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print('ok') ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(callback,queue='MQ1',no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
2. durable 消息不丢失
生产者消息持久化
import pika # ######################### 生产者 ######################### connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() # 消息持久化 channel.queue_declare(queue='MQ2', durable=True) channel.basic_publish(exchange='',routing_key='MQ2',body='Hello World!', properties=pika.BasicProperties( delivery_mode=2, # 消息持久化 )) print(" [x] Sent 'Hello World!'") connection.close()
import pika # ########################## 消费者 ########################## connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='MQ2', durable=True) # 消息持久化 def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print('ok') ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(callback,queue='MQ2',no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
3、消息获取的顺序
默认情况下,消息队列里的数据是按顺序被拿走的,例如消费者1去任务里获取奇数位置的消息队列,消费者2去任务里获取偶数位置消息队列。如果消息队列只管按顺序将消息发送到消费者身上不考虑消费者负载的话,一个配置不高的消费者堆积了一对消费不完的消息, 同事配置高的消费者却一直很轻松,为了解决该问题,在消费者端配置prefetch=1
,意思就是告诉生产者我这当前消息还没处理完的时候就不要给我发消息了。
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='MQ2',durable=True) # 消息持久化 def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print('ok') ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(callback, queue='hello', no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
4. 发布订阅
发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,实际上是先发送给exchange。
exchange=fanout
import pika,sys ##------------------------发布者----------------- connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='logs',type='fanout') message = ' '.join(sys.argv[1:]) or "info: Hello World!" channel.basic_publish(exchange='logs',routing_key='',body=message) print(" [x] Sent %r" % message) connection.close()
import pika ##------------------------订阅者----------------- connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='logs',type='fanout') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue #获取随机队列名 channel.queue_bind(exchange='logs',queue=queue_name) print(' [*] Waiting for logs. To exit press CTRL+C') def callback(ch, method, properties, body): print(" [x] %r" % body) channel.basic_consume(callback,queue=queue_name,no_ack=True) channel.start_consuming()
5、关键字发送
exchange type = direct
RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。
import pika # ######################### 生产者 ######################### connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs',type='direct') message = 'Hello World!' channel.basic_publish(exchange='direct_logs', routing_key= 'mingyue', #绑定的关键字 body=message) print(" [x] Sent %r" % (message)) connection.close()
import pika # # ########################## 消费者 ########################## connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs',type='direct') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue # 绑定两个不同的关键字 channel.queue_bind(exchange='direct_logs',queue=queue_name,routing_key= 'shuoming') channel.queue_bind(exchange='direct_logs',queue=queue_name,routing_key= 'mingyue') 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()
6、关键字匹配
exchange type = topic
在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。
-
# 表示可以匹配 0 个 或 多个 单词
-
* 表示只能匹配 一个 单词
import pika # ######################### 生产者 ######################### connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs',type='topic') message = 'Hello World!' channel.basic_publish(exchange='topic_logs', routing_key= 'www.baidu.com', #匹配模糊关键字 body=message) print(" [x] Sent %r" % (message)) connection.close()
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs',type='topic') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue # 绑定两个不同的关键字 channel.queue_bind(exchange='topic_logs',queue=queue_name,routing_key= 'www.*') 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()