Python学习之rabbitMQ使用
一对一的MQ 消费者代码实现 import pika,time connection = pika.BlockingConnection(pika.ConnectionParameters( 'localhost')) channel = connection.channel() # You may ask why we declare the queue again ‒ we have already declared it in our previous code. # We could avoid that if we were sure that the queue already exists. For example if send.py program # was run before. But we're not yet sure which program to run first. In such cases it's a good # practice to repeat declaring the queue in both programs. channel.queue_declare(queue='hello',durable=True) #生产者消费者都要声明,因为不知道谁的程序先执行 #管道的内存对象地址, def callback(ch, method, properties, body): #回调函数,消息来了就调用这个函数,函数执行完就代表消息处理完 print("--->",ch,method,properties) time.sleep(30) #模拟处理的过程中消费者断电了,突然断电了,rabbitmq不管消费者是否收到,默认客户端消息处理完了就发消息给服务端确认,,需要手动确认,生产者才把消息删除 # 默认只要客户端没确认,rabbitmq生产者不会把消息删掉 print(" [x] Received %r" % body) #手动确认 ch.basic_ack(delivery_tag=method.delivery_tag) # 可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了。 channel.basic_qos(prefetch_count=1) #队列里如果还有1条以上的消息,服务端下次不会发送消息给这个客户端 #消费者突然断了,服务端怎么知道,因为建立的是socket连接,socket断了自然知道 channel.basic_consume(#消费消息 callback,#如果收到消息,就调用callback函数来处理消息 queue='hello',#从哪个队列里收消息 # no_ack=True#处理完、没处理完都不会给服务端发消息确认 ) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming() #rabbitmqctl.bat list_queue 查看当前队列数量 # 默认服务端挂了,消息队列全部清空 生产者代码实现 import pika #通过这个实例建立socket connection = pika.BlockingConnection( pika.ConnectionParameters('localhost') ) channel = connection.channel() #声明一个管道,在这里发消息 # 声明queue channel.queue_declare(queue='hello',durable=True) #开启队列持久化,消息没有持久化,2端都要开启 # n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange. channel.basic_publish(exchange='', routing_key='hello', #queue名字 body='Hello World!', properties = pika.BasicProperties( delivery_mode=2, # make message persistent消息持久化 ) ) print(" [x] Sent 'Hello World!'") connection.close() #只持久化消息,不持久化队列,消息在下次消费者启动时还是会发送 #默认采用轮询,多个消费者,一个生产者,生产者生产消费者轮询 #server端给客户端发消息的时候先检查还有多少条消息,如果队列里消息超过1就不发,客户端才需要添加代码
广播MQ,可以自己去查看官方文档https://www.rabbitmq.com/tutorials/tutorial-three-python.html
RabbitMQ消息传递模型中的核心思想是生产者从不将任何消息直接发送到队列。实际上,生产者经常甚至根本不知道是否将消息传递到任何队列。相反,生产者只能将消息发送到交换机
交流是一件非常简单的事情。 一方面,它接收来自生产者的消息,另一方面,将它们推入队列。 交易所必须确切知道如何处理收到的消息。 是否应将其附加到特定队列? 是否应该将其附加到许多队列中? 还是应该丢弃它。 规则由交换类型定义。
Exchange在定义时是分类型的,以此来指定哪些队列可以接收消息
fanout: 所有绑定到此exchange的队列都可以接收消息
fanout消费者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- 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名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除 queue_name = result.method.queue print("random queuename",queue_name) # 创建了一个fanout exchange和一个队列。 # 现在我们需要告诉exchange将消息发送到我们的队列。 # 交换和队列之间的关系称为绑定 channel.queue_bind(exchange='logs',#绑定转发器exchange #exchange把消息放到queue里,消费者只能从queue取消息 queue=queue_name) #绑定的时候告诉exchange queue的名字 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()
fanouts生产者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- import pika import sys #广播发消息是实时的,如果发的时候你没在收就没了 # 广播不需要声明queue 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()
direct: 通过routingKey和exchange决定哪个queue可以接收消息
RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列
消费者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- import pika import sys 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 severities = sys.argv[1:] #获取所有执行脚本跟的参数,获取的是一个列表 if not severities: sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0]) sys.exit(1) print(severities) for severity in severities: channel.queue_bind(exchange='direct_logs', queue=queue_name, routing_key=severity) 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() #python direct_subscriber.py info warning error ,接收全部级别的消息
生产者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- # RabbitMQ还支持根据关键字发送,即:队列绑定关键字, # 发送者将数据根据关键字发送到消息exchange,exchange根据关键字 # 判定应该将数据发送至指定队列。 # c1只收error # c2收info error warning,都收 import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', type='direct') severity = sys.argv[1] if len(sys.argv) > 1 else 'info' message = ' '.join(sys.argv[2:]) or 'Hello World!' #把参数当成字符串发送 channel.basic_publish(exchange='direct_logs', routing_key=severity, body=message) print(" [x] Sent %r:%r" % (severity, message)) connection.close() #python direct_publisher.py warning from www #python direct_publisher.py error from www2
topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
表达式符号说明:#代表一个或多个字符,*代表任何字符
例:#.a会匹配a.a,aa.a,aaa.a等
*.a会匹配a.a,b.a,c.a等
注:使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout
当在绑定中不使用特殊字符“ * ”(星号)和“ # ”(哈希),topic类型的exchange就像direct一样。
发送到topic exchange的消息不能具有任意的 routing_key它必须是单词列表,以点分隔。这些词可以是任何东西,但通常它们指定与消息相关的某些功能。
根据级别来接收消息
消费者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- import pika import sys 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 binding_keys = sys.argv[1:] if not binding_keys: sys.stderr.write("Usage: %s [binding_key]...\n" % 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() # python topic direct_subscriber.py *.info mysql.* *.error # python topic direct_subscriber.py # //#代表收所有
生产者代码实现 #!/usr/bin/env python #-*-coding:utf8-*- 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() # python topic_publisher.py # python topic_publisher.py test.error