rabbitmq中文教程python版 - 发布 / 订阅

本文介绍了RabbitMQ中的发布/订阅模式,并通过一个简单的日志系统实例详细讲解了其核心概念,包括交换机、队列绑定及临时队列等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码:https://github.com/ltoddy/rabbitmq-tutorial

发布 / 订阅

(using the Pika Python client)

本章节教程重点介绍的内容

在上一篇教程中,我们创建了工作队列。工作队列背后的假设是每个任务只能传递给一个工作人员。
在这一部分,我们将做一些完全不同的事情 - 我们会向多个消费者传递信息。这种模式被称为“发布/订阅”。

为了说明这种模式,我们将建立一个简单的日志系统。它将包含两个程序 - 第一个将发送日志消息,第二个将接收并打印它们。

在我们的日志系统中,接收程序的每个运行副本都会收到消息。这样我们就可以运行一个接收器并将日志指向磁盘; 同时我们将能够运行另一个接收器并在屏幕上查看日志。

一般来说,发布的日志消息将以广播的形式发给所有的接收者。

交易所

在本教程的前几部分中,我们发送消息并从队列中接收消息。现在是时候在rabbitmq中引入完整的消息传递模型。

让我们快速回顾一下前面教程中的内容:
- 生产者是发送消息的用户的应用程序。
- 队列是存储消息的缓冲器。
- 消费者是接收消息的用户的应用程序。

RabbitMQ中的消息传递模型的核心思想是生产者永远不会将任何消息直接发送到队列中。实际上,生产者通常甚至不知道邮件是否会被传送到任何队列中。

相反,生产者只能发送消息给交易所。交换是一件非常简单的事情。一方面它接收来自生产者的消息,另一方则推动他们排队。
交易所必须知道如何处理收到的消息。是否应该附加到特定队列?它应该附加到许多队列中吗?或者它应该被丢弃。这些规则由交换类型定义 (exchange type)。

image

有几种可用的交换类型: direct, topic, header 和 fanout。我们将关注最后一个 - fanout。让我们创建该类型的交换,并将其称为logs:

channel.exchange_declare(exchange='logs',
                         exchange_type='fanout')

fanout交换非常简单。正如你可能从名字中猜出的那样,它只是将收到的所有消息广播到它所知道的所有队列中。这正是我们logger所需要的。

现在,我们可以发布到我们的指定交易所:

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

临时队列

正如你以前可能记得我们正在使用具有指定名称的队列(还记得hellotask_queue吗?)。能够命名队列对我们至关重要 - 我们需要将工作人员指向同一队列。
当你想在生产者和消费者之间分享队列时,给队列一个名字是很重要的。

但是,我们的记录器并非如此。我们希望听到所有日志消息,而不仅仅是其中的一部分。我们也只对目前流动的消息感兴趣,而不是旧消息。要解决这个问题,我们需要做两件事。

首先,每当我们连接到rabbitmq,我们需要一个新的,空的队列。要做到这一点,我们可以创建一个随机名称的队列,或者甚至更好 - 让服务器为我们选择一个随机队列名称。
我们可以通过不将队列参数提供给queue_declare来实现这一点:

result = channel.queue_declare()

此时,result.method.queue包含一个随机队列名称。例如,它可能看起来像amq.gen-i94oCE_tj3LyWsy-94KXHg

其次,一旦消费者连接关闭,队列应该被删除。这是一个专有标志:

result = channel.queue_declare(exclusive=True)

绑定

image

我们已经创建了一个fanout交换和一个队列。现在我们需要告诉交换所将消息发送到我们的队列。交换和队列之间的关系称为绑定。

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

从现在起,logs 交易所会将消息附加到我们的队列中。

把它放在一起

image

发出日志消息的生产者程序与之前的教程没有多大区别。最重要的变化是我们现在想发布消息到我们的logs交易所,而不是无名字的消息。发送时我们需要提供一个routing_key,但是对于fanout交换,它的值将被忽略。这里是emit_log.py脚本的代码 :

#!/usr/bin/env python
import sys
import pika

connection = pika.BlockingConnection(
    pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='logs',
                         exchange_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()

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

如果没有队列绑定到交换机上,这些消息将会丢失,但这对我们来说没问题; 如果没有消费者正在收听,我们可以放心地丢弃消息。

receive_logs.py的代码:

#!/usr/bin/env python
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='logs',
                         exchange_type='fanout')

result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue

channel.queue_bind(exchange='logs',
                   queue=queue_name)

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()

我们完成了。如果您想将日志保存到文件中,只需打开一个控制台并输入:

python receive_logs.py > logs_from_rabbit.log

如果你想在屏幕上看到日志,打开一个新的终端并运行:

python receive_logs.py

当然,

python emit_log.py

使用rabbitmqctl list_bindings,你可以验证代码是否真正创建了绑定和队列。当有两个receive_logs.py程序正在运行,你应该看到如下所示:

root@921edcb46341:/# rabbitmqctl list_bindings
Listing bindings for vhost /...
    exchange    amq.gen-6YXn7BycIwtI7kFuUrTbaA  queue   amq.gen-6YXn7BycIwtI7kFuUrTbaA  []
    exchange    amq.gen-JhFL-rbMAoricMu5Dyo-hA  queue   amq.gen-JhFL-rbMAoricMu5Dyo-hA  []
logs    exchange    amq.gen-6YXn7BycIwtI7kFuUrTbaA  queue   amq.gen-6YXn7BycIwtI7kFuUrTbaA  []
logs    exchange    amq.gen-JhFL-rbMAoricMu5Dyo-hA  queue   amq.gen-JhFL-rbMAoricMu5Dyo-hA  []

image

<think>我们正在检查RabbitMQ日志目录的权限和SELinux上下文信息用户提供的目录是:/static/rabbitmq/logs之前已经通过ls -ldZ查看了该目录:drwx------2root root?4096Jul310:53 /static/rabbitmq/logs这里的关键信息:权限:700 (只有所有者有读写执行权限)所有者:root所属组:rootSELinux上下文:?表示可能没有上下文或者我们的命令没有显示但是RabbitMQ服务通常以rabbitmq用户身份运行,因此需要确保rabbitmq用户对该目录有写权限。解决步骤:1.更改目录所有者为rabbitmq用户和组2.确保目录权限至少为750(所有者有读写执行,组有读和执行)或者770(组有读写执行)以便rabbitmq用户可以写入3.检查SELinux上下文,确保正确设置另外,还需要检查父目录的权限,因为要访问/static/rabbitmq/logs,需要/static、/static/rabbitmq目录至少有执行权限(x)给rabbitmq用户。根据引用[1]中提到的,RabbitMQ的日志目录可以通过RABBITMQ_LOG_BASE环境变量设置,所以确保这个环境变量指向了/static/rabbitmq/logs。操作步骤:步骤1:检查父目录权限我们需要确保从根目录开始到logs目录的每一层目录,rabbitmq用户都有执行权限(x)。步骤2:更改logs目录的所有者和权限将所有者改为rabbitmq,并设置合适的权限。步骤3:检查SELinux上下文如果SELinux是开启状态,那么需要给这个目录设置正确的上下文类型(例如rabbitmq_log_t),或者暂时禁用SELinux(不推荐)或者设置SELinux策略允许访问。具体命令:1.检查父目录权限(以/static/rabbitmq为例):-检查/static目录:`ls -ld /static`-检查/static/rabbitmq目录:`ls-ld/static/rabbitmq`要求:其他用户(至少)需要有执行权限(x)才能进入目录。如果rabbitmq用户不属于root组,那么可能需要给其他用户开放执行权限(o+x)或者将组改为rabbitmq组并给组加权限。2.修改logs目录权限:```bashsudochown-Rrabbitmq:rabbitmq/static/rabbitmq/logssudo chmod -R750 /static/rabbitmq/logs#或者770,如果目录中还有需要其他用户访问的情况,根据实际情况调整```3.检查SELinux状态:```bashsestatus```如果SELinux是开启的(enabled),那么需要设置目录的SELinux上下文:```bash#首先查看默认的RabbitMQ日志目录的上下文ls -Zd /var/log/rabbitmq#通常为rabbitmq_log_t#然后设置我们的目录为同样的上下文sudosemanage fcontext -a -t rabbitmq_log_t "/static/rabbitmq/logs(/.*)?"sudo restorecon -Rv /static/rabbitmq/logs```4.验证:以rabbitmq用户身份尝试在目录中创建文件```bashsudo -u rabbitmq touch/static/rabbitmq/logs/testfile```如果成功,则权限和SELinux设置正确。注意:如果RabbitMQ是通过systemd服务运行的,确保服务配置中环境变量RABBITMQ_LOG_BASE指向了/static/rabbitmq/logs。通常可以在/etc/rabbitmq/rabbitmq-env.conf中设置(如引用[1]所示)。如果问题仍然存在,请检查RabbitMQ的日志(如果可能的话,可能是原来的日志位置/var/log/rabbitmq/rabbit@*.log)来获取更多错误信息。根据以上步骤,我们可以解决RabbitMQ日志目录的权限问题。</think>### RabbitMQ 日志目录权限检查指南 根据您提供的目录信息 `/static/rabbitmq/logs` 和引用内容[^1],以下是完整的权限检查流程: #### 1. 当前权限状态分析 ```bash $ ls -ldZ /static/rabbitmq/logs drwx------ 2 root root ? 4096 Jul 3 10:53 /static/rabbitmq/logs ``` - **权限问题**:`drwx------` 表示仅 root 用户有读写权限 - **所有者问题**:目录所有者是 root,但 RabbitMQ 服务以 `rabbitmq` 用户运行 - **SELinux问题**:`?` 表示未设置正确的安全上下文 #### 2. 权限修复步骤 ```bash # 停止服务 sudo systemctl stop rabbitmq-server # 修复所有者和权限 sudo chown -R rabbitmq:rabbitmq /static/rabbitmq/logs sudo chmod 750 /static/rabbitmq/logs # 组用户增加读取权限 # 检查父目录权限(关键步骤) sudo namei -l /static/rabbitmq/logs # 确保路径中每个目录都有执行权限(x) sudo chmod o+x /static /static/rabbitmq # 添加其他用户执行权限 # 设置SELinux上下文 sudo semanage fcontext -a -t rabbitmq_log_t "/static/rabbitmq/logs(/.*)?" sudo restorecon -Rv /static/rabbitmq/logs # 验证RabbitMQ用户写入能力 sudo -u rabbitmq touch /static/rabbitmq/logs/testfile && echo "成功" || echo "失败" ``` #### 3. 配置文件检查 确保 `/etc/rabbitmq/rabbitmq-env.conf` 包含正确配置(引用[^1]): ```conf RABBITMQ_LOG_BASE=/static/rabbitmq/logs ``` #### 4. 服务重启与验证 ```bash sudo systemctl start rabbitmq-server sudo rabbitmqctl status # 检查服务状态 tail -f /static/rabbitmq/logs/rabbit@*.log # 查看实时日志 ``` #### 关键原理说明 1. **目录遍历权限**:RabbitMQ 进程需要路径中所有父目录的执行权限(x)才能访问日志目录 2. **SELinux 上下文**:必须设置为 `rabbitmq_log_t` 类型(引用[^1]) 3. **所有者匹配**:日志目录所有者必须与 RabbitMQ 服务运行用户一致(通常是 `rabbitmq`) 4. **配置一致性**:`RABBITMQ_LOG_BASE` 必须与实际路径匹配(引用[^1]) > **注意**:如果使用 Docker,需确保卷映射正确: > ```bash > docker run -d \ > -v /static/rabbitmq/logs:/var/log/rabbitmq \ > -e RABBITMQ_LOG_BASE=/var/log/rabbitmq \ > rabbitmq:management > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值