RabbitMQ操作
注意:在rabbitmq中,可以存在多个exchange,exchange只是负责接收消息,然后消息必须发送到给queue中,如果没有queue,消息就丢失了,exchange就相当于交换机,不负责存消息,queue是必须声明的,所以exchange负责转发,queue负责接收
回调函数:
普通函数与回调函数的区别:
-
对普通函数的调用:
调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返回调用程序继续执行。从发出调用的程序的角度看,这个过程为“调用-->等待被调用函数执行完毕-->继续执行” -
对回调函数调用:
调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来。 -
比如你显示器不亮了,你不知道怎么弄,那你就问在外地干IT的大表哥,你大表哥告诉你该怎么弄,然后需要你自己来操作。
你大表哥知道怎么弄,但是不会自己去弄,而是由你去弄。
换句话说,你大表哥实现了修理你显示器的方法,但他不会自己去调用,而是由你去调用。
那么你大表哥告诉你的修机器的方法就是回调函数。
最简单的队列通信
send端
import pika #创建一个类似于socket连接 #credentials = pika.PlainCredentials('zcq', '123456') # connection = pika.BlockingConnection(pika.ConnectionParameters(host=url_1, # credentials=credentials, ssl=ssl, port=port)) # connection = pika.BlockingConnection(pika.ConnectionParameters( # host='192.168.12.112',credentials=credentials)) connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() #商议好协议 # 声明queue channel.queue_declare(queue='zcq',durable=True) #声明Q durable为设置为持久化 Q名必须唯一 # 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='zcq', #send msg to this queue 发送到哪个Q里面 body='zhaichaoqun', #消息内容 properties=pika.BasicProperties( delivery_mode=2, # make message persistent 持久化 如果Q设置为持久化 消息也必须设置持久化 ) ) print(" [x] Sent 'Hello World!2'") connection.close()
send端开头部分注释: 是设置密码验证,如果rabbitmq 开启了用户认证形式,就必须指定用户名密码
recv端
import pika import time #如果有密码就用密码认证 #credentials = pika.PlainCredentials('alex', 'alex3714') # connection = pika.BlockingConnection(pika.ConnectionParameters( # host='192.168.12.112',credentials=credentials)) connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.queue_declare(queue='zcq',durable=True) def callback(ch, method, properties, body): print(ch, method, properties) print(" [x] Received %r" % body) time.sleep(1) channel.basic_consume(callback, queue='zcq', #no_ack=True #ack知识 ) channel.basic_qos(prefetch_count=1) #相当于负载均衡 print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。为解决此问题,可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了
消息的发送和订阅
exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件
有三种类型:
fanout: 所有bind到此exchange的queue都可以接收到消息
direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
topic:所有符合routingKey(也可以是一个表达式)的 routingKey的bind的queue可以接收消息
表达式: # 代表一个或多个字符, *代表任何字符,如果routingKey直接设置为#,那这里的topic类型,就相当于使用了fanout
例如: #.a 会匹配到a.a aa.a aaa.a 等
*.a 会匹配到A.a b.a asas.a 等
Exchange:根据Routing key 转发消息到对应的Message Queue中
RoutingKey: 用于Exchange 判断哪些消息需要发送到对应的Message Queue
消息订阅和发布(exchange type=fanout)
send端
import pika import sys #生产者 # #如果需要密码 这里指定密码 #credentials = pika.PlainCredentials('guest','guest') #connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',credentials=credentials )) connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() #channel 绑定的意思,即routing key,就好比,每个邮局把信件送到你家邮箱的一条路线 channel.exchange_declare(exchange='zcq', type='fanout') message = ' '.join(sys.argv[1:]) or 'hellow-----' channel.basic_publish(exchange='zcq', routing_key='', body=message) print('--%s--'%message) connection.close()
运行:python3 rabbit-fanout.py info test 发送info test 到订阅的消费者中
recv端
import pika # credentials = pika.PlainCredentials('guest','guest') connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='zcq',type='fanout') result=channel.queue_declare(exclusive=True) #不指定Q名,rabbit会随机分配一个名字,这里会在使用queue的消费着断开后,自动删除q queue_name = result.method.queue #得到队列的名字 channel.queue_bind(exchange='zcq',queue=queue_name) def callback(ch, method, properties, body): #def callback(ch,method,properties,body): print('[x] %r' %body) channel.basic_consume(callback,queue=queue_name) channel.start_consuming()
运行:python3 rabbit-fanout-recv.py info recv端接受info信息
有选择的接受消息(exchange type=direct)
send端
import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', #设置exchange名称 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() #
运行:>python3 rabbit-direct-send.py info aa 注释:send端将 sys.argv[1] 值设置为routingKey 后面aa为信息
recv端
import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', #指定exchange名称 type='direct') result = channel.queue_declare(exclusive=True) ##不指定Q名,rabbit会随机分配一个名字,这里会在使用queue的消费着断开后,自动删除q queue_name = result.method.queue #得到Q名称 recv端必须指定queue severities = sys.argv[1:] if not severities: sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0]) sys.exit(1) for severity in severities: #循环 severities 列表 得到用户输入值 然后传入到routing_key 来绑定 channel.queue_bind(exchange='direct_logs', queue=queue_name, routing_key=severity) #exchange 接收到消息。然后把消息交给Q里,这里是指定Q,如果不指定,就丢失了消息 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() #开始接收
运行:python3 rabbit-direct-recv.py aa 注释:recv端开启一个以aa为routingKey的信息
所以send开启info recv端也必须开启info,
更细致的消息过滤(exchange type=topic)
send端
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()
recv端
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()