rabbitmq理论知识
可以当作一个邮局,是一个消息队列,生产者将消息放入队列,消费者在队列中取消息。rabbitqm是一个服务器,不同的程序都可以通过rabbitmq来进行通讯。
1.队列queue
不同的程序可以确定不同的队列来进行信息的传输,生产者将消息放在队列中,消费者从队列中获取信息。多个消费者可以同时从队列中取数据,而且rabbitmq是公平的,给多个消费者平均的发消息,而不是像线程一样来抢资源。
1.1 多个消费者可能出现的问题
1)若消费者没有处理完消息就宕机,则有可能会导致这条消息丢失,为了避免这种情况,每个消费者完成了一个消息,服务器会检测消费者的状态,是否断开了连接,消费者都必须给rabbitmq服务器返回一个回执来进行确定,此时rabbitmq才会从队列中删除这个消息,但是只要消费者没有断开连接,且没有返回回执,则这个消息一直在队列中。
2)若多个消费者处理消息的能力不同,当第一个消费者刚处理一半时,又轮到给这个消费者发消息,则会出现问题,
可以通过设置prefetchcount来设置每次给消费者的消息数,每次只给一个,这样当检测到正在处理时,则不会给他发消息。
2.exchange
在rabbitmq中,实际上服务器并不是直接将消息放入队列,而是将消息放入交换机,交换机根据不同的规则,来确定发送的消费者
2.1 rounting key
生产者在将消息发送给ex的时候,一般会指定一个rounting key,也就是发送规则,
2.2 binding
binding的作用是将交换机和队列关联起来,这样就可以通过交换机放队列里发数据
1.消息确认
当处理一个比较耗时得任务的时候,你也许想知道消费者(consumers)是否运行到一半就挂掉。当前的代码中,当消息被RabbitMQ发送给消费者(consumers)之后,马上就会在内存中移除。这种情况,你只要把一个工作者(worker)停止,正在处理的消息就会丢失。同时,所有发送到这个工作者的还没有处理的消息都会丢失。
我们不想丢失任何任务消息。如果一个工作者(worker)挂掉了,我们希望任务会重新发送给其他的工作者(worker)。
为了防止消息丢失,RabbitMQ提供了消息响应(acknowledgments)。消费者会通过一个ack(响应),告诉RabbitMQ已经收到并处理了某条消息,然后RabbitMQ就会释放并删除这条消息。
如果消费者(consumer)挂掉了,没有发送响应,RabbitMQ就会认为消息没有被完全处理,然后重新发送给其他消费者(consumer)。这样,及时工作者(workers)偶尔的挂掉,也不会丢失消息。
消息是没有超时这个概念的;当工作者与它断开连的时候,RabbitMQ会重新发送消息。这样在处理一个耗时非常长的消息任务的时候就不会出问题了。
消息响应默认是开启的。之前的例子中我们可以使用no_ack=True标识把它关闭。是时候移除这个标识了,当工作者(worker)完成了任务,就发送一个响应。
def callback(ch, method, properties, body): print " [x] Received %r" % (body,) time.sleep( body.count('.') ) print " [x] Done" ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(callback, queue='hello')
2.消息持久化
如果你没有特意告诉RabbitMQ,那么在它退出或者崩溃的时候,将会丢失所有队列和消息。为了确保信息不会丢失,有两个事情是需要注意的:我们必须把“队列”和“消息”设为持久化。
首先,为了不让队列消失,需要把队列声明为持久化(durable):
channel.queue_declare(queue='hello', durable=True)
这时候,我们就可以确保在RabbitMq重启之后queue_declare队列不会丢失。另外,我们需要把我们的消息也要设为持久化——将delivery_mode的属性设为2。
channel.basic_publish(exchange='', routing_key="task_queue", body=message, properties=pika.BasicProperties( delivery_mode = 2, # make message persistent ))
3.公平调度
我们可以使用basic.qos方法,并设置prefetch_count=1。这样是告诉RabbitMQ,再同一时刻,不要发送超过1条消息给一个工作者(worker),直到它已经处理了上一条消息并且作出了响应。这样,RabbitMQ就会把消息分发给下一个空闲的工作者(worker)。
channel.basic_qos(prefetch_count=1)
===
整合代码
发送者
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))#创建连接
channel = connection.channel()#创建队列
channel.queue_declare(queue='task_queue', durable=True)#声明队列并且持久化
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
routing_key='task_queue',
body=message,
properties=pika.BasicProperties(
delivery_mode = 2, # make message persistent
))#持久化
print " [x] Sent %r" % (message,)
connection.close()
#已经去掉no_ask 所以会有反馈
消费者
#!/usr/bin/env python import pika import time connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost'))#创建连接 channel = connection.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 " [x] Received %r" % (body,) time.sleep( body.count('.') ) print " [x] Done" ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_qos(prefetch_count=1)#公平调度 channel.basic_consume(callback, queue='task_queue') channel.start_consuming()
发布订阅
将一条消息分发给多个接收者,类似于广播。
生产者:
登录rabbitmq服务器,建立队列,声明交换机名称和类型,确定消息类型,将消息发送到交换机,
消费者:
登录到rabbitmq服务器,建立队列,声明交换机类型和名称,建立临时队列,名称,使用后删除,将队列和交换机进行绑定,进行接收,制定回调函数,