Exchange:
1、fanout:
广播模式:每个与此Exchange绑定的Queue,都会接收此Exchange的消息。
之前提到的RabbitMQ消息分发中的channel.basic_publish 中的 exchange ='',这意味着将Producer发送的Message直接发送给名字为'hello'的队列中,而在Consumer则通过指定的队列名称来获取数据,但队列中的数据只有一份,如果想多个Consumer都可以得到相同数据的话,就需要每个Consumer都创建一个队列来获取数据,而原Consumer中声明的方法只会在RabbitMQ中创建一个名为'hello'的队列。
channel.queue_declare(queue = 'hello')
(1)创建队列:Consumer中声明队列的时候如果不给队列设置名称,RabbitMQ就会默认的给生成一个随机名称,而这个名称可以通过method.queue来获取。
result = channel.queue_declare(exclusive = True) #exclusive参数设置为True 是因为,如果Consumer关闭连接的话,就会queue就会被delete掉,所以设置成True. queue_name = result.method.queue print('queue name =',queue_name) 控制台输出 >>> queue name = amq.gen-LJjflCJqIW60S183_vUnJg
(2)创建Exchange:通过exchange_declare()方法就可以创建一个exchange.
channel.exchange_declare(exchange='logs',type = 'fanout') # 参数 exchange 为声明的exchange的名字,type为exchange的类型这里这职位广播模式fanout。
(3)绑定:通过queue_bind将Exchange和queue绑定到一块
channel.queue_bind(exchange='log',queue = queue_name)
Producer:
(1)声明Exchange:通过exchange_declare()方法就可以创建一个exchange.
channel.exchange_declare(exchange='logs',type = 'fanout')
(2)通过basic_publish发送消息
channel.basic_publish(exchange = 'log', # 指定exchang routing_key = '', # 因为是通过exchange来发送消息,这里只需要为空 body = stdin_str, )
完整版:
Producer:
import pika # 创建一个connection connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) # 创建一个管道 channel = connection.channel() channel.exchange_declare(exchange='log',type='fanout') while True: stdin_str = input('>>>') channel.basic_publish(exchange = 'log', routing_key = '', body = stdin_str, ) print(" [x] Sent '{}'".format(stdin_str)) connection.close()
Consumer:
import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) #创建一个管道 channel = connection.channel() #声明QUEUE result = channel.queue_declare(exclusive = True) # 获取queue名字 queue_name = result.method.queue #声明一个Exchange 这指定名称为log,类型为fanout channel.exchange_declare(exchange='log',type = 'fanout') # 绑定Exchange和Queue channel.queue_bind(exchange='log',queue = queue_name) #回调函数 def callback(ch,method,properties,body): print(" [x] Received %r" % body.decode()) channel.basic_consume(callback, queue =result.method.queue, no_ack = False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.basic_qos(prefetch_count=1) #让Consumer谁执行完谁去Queue中领任务执行,以达到资源合理分配。 channel.start_consuming()
运行Producer,再运行多个Consumer后,当Producer发送消息的时候,这些Consumer都会接受到消息。
2、Direct:
Direct Exchange就非常的简单了 ,他是通过绑定routing_key来给不同的队列发送消息。
(1)声明Exchange类型:
channel.exchange_declare(exchange = 'direct_logs',type = 'direct')
(2)绑定routing_key:
在 queue_bing 中绑定队列和Exchange的时候指定 routing_key 的名称。
channel.queue_bind(exchange = 'log',queue = queue_name, routing_key = 'task')
(3)在Producer 的 publish中指定routing_key的名称
channel.basic_publish(exchange='direct_logs',routing_key='task',body=message)
3、通过不同的 routing_key 分发消息 info、warning、error
Producer:
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()
Consumer:
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) 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()
3、Topic:主题模式
如果说我们想实现的那种类似python中logger日志的功能,给日志分等级、分各个模块的话,我们就需要用到了Topic模式;在Topic中,我们是通过设置routing_key来区分我们的消息,routing_key的名称规范:用 . 来进行分割编写 ,如:logs.info 、config.error、bin.warning;并且在Topic模块中,可以用特殊字符来区分接收的信息的范围:
* 代表任意 一个单词 *.info、error.*
# 0个或者多个单词 # 、info.#
需要注意的是:如果绑定的routing_key 为 # ,代表着匹配所有的信息。
Publisher:
import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs', type='topic') # 设置exchange的类型为topic routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info' # 设置默认主题为.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()
subscriber:
import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs', type='topic') # 声明exchange的类型为topit result = channel.queue_declare(exclusive=True) queue_name = result.method.queue binding_keys = sys.argv[1:] # 在控制台输入参数info、 warning、error 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) #绑定 routing_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()