基于Python语言使用RabbitMQ消息队列(三)

释放双眼,带上耳机,听听看~!

发布/订阅

前面的教程中我们已经创建了一个工作队列。在一个工作队列背后的假设是每个任务恰好会传递给一个工人。在这一部分里我们会做一些完全不同的东西——我们会发送消息给多个消费者。这就是所谓的“发布/订阅”模式。

为了解释这种模式,我们将会构建一个简单的日志系统。它包含两个程序——第一个产生日志消息,第二个接收并把他们打印出来。

在我们的日志系统中,每一个接收程序的正在运行的拷贝都会获知消息,那样我们将能够运行一个接收者把日志指向磁盘;同时我们将能够运行另一个接收者在屏幕上查看日志。

实质上,被发布的消息将会广播给所有接收。

交易所

在前面的教程中我们向一个队列中发送和接收消息. 现在来介绍Rabbit中的完整的消息模型 .

我们快速回顾一下前面的教程:

  • 生产者是一个发送消息的用户应用。
  • 队列是一个存储消息的缓冲区。
  • 消费者是一个接收消息的用户应用。

RabbitMQ中消息模型的核心思想是,生产者从不直接发送任何消息给一个队列。实际上,生产者通常甚至一点都不知道一条消息会被发送给什么队列。
相反,生产者只能发送消息给一个交易所(exchange)。交易所是一个很简单的东西。它一面接收来自生产者的消息,一面把消息推送给队列。交易所必须准确知道如何对待它接收到的消息。它该被追加到一个特定队列中吗?还是应该把它追加到多个队列?又或者它该被忽略?这些规则都是由交易类型(exchange type)所定义的。
有几种可用的交易类型: direct, topic, headers 和 fanout. 我们会关注最后一个——fanout. 我们来创建一个这种类型的交易所,命名为 logs:


1
2
3
1channel.exchange_declare(exchange='logs',
2                         type='fanout')
3

fanout类型交易所非常简单。从名字中你可能已经猜到(其实英语非母语的也不大好猜到,fan(风扇)+out(外、出)的组合,我觉得在这里可以理解为扇出去、扬出去的意思),它只是广播它接到的所有消息给它知道的所有队列 ,这恰恰是我们的日志程序所需要的。

列出所有交际所

为了列出所有交易所,你可以运行Trabbitmqctl:

sudo rabbitmqctl list_exchanges 在这个列表中会有些像amq.* 的交易所和默认的(未命名的)交易所
这些是被默认创建的,但你目前不太可能需要用到它们。

默认交易所

在之前的教程中我们队交易所一无所知。但仍然能够给队列发送消息。这是因为我们使用了默认交易所,我们通过空字符串 (“”)来标识。

回顾一下我们之前如何发布一条消息:

channel.basic_publish(exchange='',
routing_key='hello',
body=message)

exchange 参数就是交易所的名字。空字符串代表默认或者无名交易所:
消息路由到名字被routing_key所指定的队列,如果队列存在的话。

现在我们可以把消息发布到命名交易所了:


1
2
3
4
1channel.basic_publish(exchange='logs',
2                      routing_key='',
3                      body=message)
4

临时队列

你可能记得我们先前使用了有特定名字的队列 (记得 hello 和 task_queue?)。能给队列命名对我们来说很关键——我们需要把工人们指向同一个队列。当你想要在生产者和消费者中分享队列时,给队列一个名字就很重要。

但那不符合我们的日志程序需要。我们想获取所有日志消息,不只是他们的一个子集。我们也仅对当前活跃的消息感兴趣,而不是旧有的,解决这个问题我们需要做两件事。

第一,无论何时连接到Rabbit我们都需要一个新鲜的(fresh)空的(empty)队列 ,为实现这一点我们使用随机的名字创建一个队列,或者更好的——让服务器随机选一个队列名给我们。 我们不给queue_declare提供队列参数就可以做到:


1
2
1result = channel.queue_declare()
2

这样的话, result.method.queue 就会包含一个任意的队列名,例如它可能看上去是这样的 : amq.gen-JzTY20BRgKO-HjmUJj0wLg.

第二, 一旦我们断开消费者链接,队列就该被删除掉。通过一个exclusive 标志实现:


1
2
1result = channel.queue_declare(exclusive=True)
2

绑定

我们已经创建了一个fanout 交易所和一个队列。现在我们要通知交易所发送消息给我们的队列。交易所和一个队列之间的关系叫做绑定。


1
2
3
1channel.queue_bind(exchange='logs',
2                   queue=result.method.queue)
3

从现在开始logs交易所会追加消息给我们的队列。

列出所有绑定

你可以列出所有存在的绑定,使用…好吧,你已经猜到了:
rabbitmqctl list_bindings

整合

用来发送日志消息的生产者程序,看起来和之前并没有多大不同。最大的变化是我们现在想要发送消息给logs交易所而不是无名交易所。当发送时我们需要提供一个routing_key,但对于fanout类型交易所来说它的值是被忽略的。 下面是emit_log.py 脚本代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1#!/usr/bin/env python
2import pika
3import sys
4
5connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
6channel = connection.channel()
7
8channel.exchange_declare(exchange='logs',
9                         type='fanout')
10
11message = ' '.join(sys.argv[1:]) or "info: Hello World!"
12channel.basic_publish(exchange='logs',
13                      routing_key='',
14                      body=message)
15print(" [x] Sent %r" % message)
16connection.close()
17

如你所见,建立完连接后我们生命了交易所。这一步是必须的,因为发布到一个不存在的交易所是被禁止的。

如果没有队列连接(bound)到交易所,消息就会丢失,但对我们来说没有关系;如果没有消费者监听我们可以安全地忽略掉这个消息。

receive_logs.py代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1#!/usr/bin/env python
2import pika
3
4connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
5channel = connection.channel()
6
7channel.exchange_declare(exchange='logs',
8                         type='fanout')
9
10result = channel.queue_declare(exclusive=True)
11queue_name = result.method.queue
12
13channel.queue_bind(exchange='logs',
14                   queue=queue_name)
15
16print(' [*] Waiting for logs. To exit press CTRL+C')
17
18def callback(ch, method, properties, body):
19    print(" [x] %r" % body)
20
21channel.basic_consume(callback,
22                      queue=queue_name,
23                      no_ack=True)
24
25channel.start_consuming()
26

大功告成!如果你想把日志保存到一个文件中只需要打开控制台,键入:


1
2
1python receive_logs.py > logs_from_rabbit.log
2

如果你想在屏幕查看日志, 打开一个新的控制台运行:


1
2
1python receive_logs.py
2

下两图分别为在我的Ubuntu终端使用cat命令输出的存入到日志文件的日志和屏幕显示的日志
图1
图2
当然需要运行emit_log.py
使用 rabbitmqctl list_bindings 你可以确认代码创建了绑定和队列,就像我们想要的那样。在两个运行的情况下你应该会看到如下内容:


1
2
3
4
5
6
1sudo rabbitmqctl list_bindings
2# => Listing bindings ...
3# => logs    exchange        amq.gen-JzTY20BRgKO-HjmUJj0wLg  queue           []
4# => logs    exchange        amq.gen-vso0PVvyiRIL2WoV3i48Yg  queue           []
5# => ...done.
6

结果的解释很直白: 来自logs交易所的数据去往两个服务器指定了名字的队列 ,这正是我们所预想的。
若想知道如何监听消息子集,请前往下一节。

给TA打赏
共{{data.count}}人
人已打赏
安全网络

CDN安全市场到2022年价值76.3亿美元

2018-2-1 18:02:50

安全运维

基于lucene的案例开发:纵横小说阅读页采集

2021-12-11 11:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索