使用php amqplib和RabbitMQ的死信?

使用php amqplib和RabbitMQ的死信?,php,rabbitmq,php-amqplib,Php,Rabbitmq,Php Amqplib,我刚刚开始使用PHPAMqplib和RabbitMQ,希望找到一种方法来处理由于任何原因无法处理且无法访问的消息。我认为人们处理这件事的一种方法是用死信队列。我正试图建立这个,但到目前为止还没有任何运气,希望有人能提供一些建议 我发起的队列看起来有点像: class BaseAbstract { /** @var AMQPStreamConnection */ protected $connection; /** @var AMQPChannel */ prote

我刚刚开始使用PHPAMqplib和RabbitMQ,希望找到一种方法来处理由于任何原因无法处理且无法访问的消息。我认为人们处理这件事的一种方法是用死信队列。我正试图建立这个,但到目前为止还没有任何运气,希望有人能提供一些建议

我发起的队列看起来有点像:

class BaseAbstract
{
    /** @var AMQPStreamConnection */
    protected $connection;
    /** @var AMQPChannel */
    protected $channel;
    /** @var array */
    protected $deadLetter = [
        'exchange' => 'dead_letter',
        'type' => 'direct',
        'queue' => 'delay_queue',
        'ttl' => 10000 // in milliseconds
    ];

    protected function initConnection(array $config)
    {
        try {
            $this->connection = AMQPStreamConnection::create_connection($config);
            $this->channel = $this->connection->channel();

            // Setup dead letter exchange and queue
            $this->channel->exchange_declare($this->deadLetter['exchange'], $this->deadLetter['type'], false, true, false);
            $this->channel->queue_declare($this->deadLetter['queue'], false, true, false, false, false, new AMQPTable([
                'x-dead-letter-exchange' => $this->deadLetter['exchange'],
                'x-dead-letter-routing-key' => $this->deadLetter['queue'],
                'x-message-ttl' => $this->deadLetter['ttl']
            ]));
            $this->channel->queue_bind($this->deadLetter['queue'], $this->deadLetter['exchange']);

            // Set up regular exchange and queue
            $this->channel->exchange_declare($this->getExchangeName(), $this->getExchangeType(), true, true, false);
            $this->channel->queue_declare($this->getQueueName(), true, true, false, false, new AMQPTable([
                'x-dead-letter-exchange' => $this->deadLetter['exchange'],
                'x-dead-letter-routing-key' => $this->deadLetter['queue']
            ]));

            if (method_exists($this, 'getRouteKey')) {
                $this->channel->queue_bind($this->getQueueName(), $this->getExchangeName(), $this->getRouteKey());
            } else {
                $this->channel->queue_bind($this->getQueueName(), $this->getExchangeName());
            }
        } catch (\Exception $e) {
            throw new \RuntimeException('Cannot connect to the RabbitMQ service: ' . $e->getMessage());
        }
        return $this;
    }

    // ...
}
我认为应该设置死信交换和队列,然后再设置常规交换和队列(使用扩展类提供的getRouteKey、getQueueName和getExchangeName/Type方法)

当我尝试处理以下消息时:

public function process(AMQPMessage $message)
{
    $msg = json_decode($message->body);
    if (empty($msg->payload) || empty($msg->payload->run)) {
        $message->delivery_info['channel']->basic_nack($message->delivery_info['delivery_tag'], false, true);
        return;
    }

    // removed for post brevity, but compose $cmd variable

    exec($cmd, $output, $returned);
    if ($returned !== 0) {
        $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
    } else {
        $message->delivery_info['channel']->basic_nack($message->delivery_info['delivery_tag']);
    }
}
但我返回了错误
出现了问题:无法连接到RabbitMQ服务:前提条件\u失败-vhost'/'中队列“delay\u queue”的参数“x-dead-letter-exchange”不等价:收到了“dead-letter”,但当前为“”

这是我设置死字的方式吗?我在各地看到的不同例子似乎都显示了一种不同的处理方式,但似乎没有一种适合我。因此,我显然误解了这里的某些内容,非常感谢您的建议。:)

设置(永久性)队列和交换是您在部署代码时想做的一次,而不是每次想使用它们时。把它们想象成您的数据库模式——尽管协议提供了“declare”而不是“create”,但您通常应该编写假定以特定方式配置的代码。您可以将代码的第一部分构建到安装脚本中,或者使用简单的JSON格式来管理这些脚本

您看到的错误可能是由于尝试在不同的时间使用不同的参数声明同一队列而导致的,“声明”不会替换或重新配置现有队列,它会将参数视为要检查的“先决条件”。您需要删除并重新创建队列,或通过管理UI对其进行管理,以更改其现有参数

当您希望在代理中动态创建项时,运行时声明变得更有用。您可以为他们指定您知道的唯一名称,或者将
null
作为名称传递,以接收随机生成的名称(人们有时指创建“匿名队列”,但RabbitMQ中的每个队列都有一个名称,即使您没有选择)


如果我读对了,您的“模式”如下所示:

# Dead Letter eXchange and Queue
Exchange: DLX
Queue: DLQ; dead letter exchange: DLX, with key "DLQ"; automatic expiry
Binding: copy messages arriving in DLX to DLQ

# Regular eXchange and Queue
Exchange: RX
Queue: RQ; dead letter exchange: DLX, with key "DLQ"
Binding: copy messages from RX to RQ, optionally filtered by routing key
当消息在RQ中被“nacked”时,它将被传递到DLX,其路由键被覆盖为“DLQ”。然后将其复制到DLQ。如果它是从DLQ请求的,或者在该队列中等待的时间太长,它将被路由到自身

我将从两个方面进行简化:

  • 从“死信队列”(我已经标记为DLQ)中删除死信交换和TTL;这个循环可能更容易混淆,而不是有用
  • 从常规队列(我已经标记为RQ)中删除
    x-dead-letter-routing-key
    选项。常规队列的配置不需要知道死信交换是否有零个、一个或多个附加到它的队列,因此不应该知道另一个队列的名称。如果您想让nacked消息直接进入一个队列,只需将其设置为“扇出交换”(忽略路由键)或“主题交换”(绑定键设置为
    #
    (与所有路由键匹配的通配符)

另一种方法是将
x-dead-letter-routing-key
设置为常规队列的名称,即标记它来自哪个队列。但是,在你有一个这样的用例之前,我会保持它的简单,并将消息与其原始路由密钥一起保留。

PS:Hi Andy!;)嗨!!:-D根本没有看到这个回复,对不起!非常好的反馈,我完全支持简化事情,所以我会尝试一下,谢谢!