web-dev-qa-db-ja.com

RabbitMqからのメッセージを確認しない消費

basic.consumeを使用してキューでサブスクライブする単純なパブリッシャーとコンシューマーを作成しました。

私の消費者は、ジョブが例外なく実行されたときにメッセージを確認します。例外が発生したときはいつでも、メッセージを確認せずに早く戻ります。確認済みのメッセージのみがキューから消えるので、正しく機能しています。
ここで、失敗したメッセージをコンシューマーに再度取得してもらいたいのですが、これらのメッセージを再開する唯一の方法は、コンシューマーを再起動することです。

このユースケースにどのようにアプローチする必要がありますか?

セットアップコード

$channel = new AMQPChannel($connection);

$exchange = new AMQPExchange($channel);

$exchange->setName('my-exchange');
$exchange->setType('fanout');
$exchange->declare();

$queue = new AMQPQueue($channel);
$queue->setName('my-queue');
$queue->declare();
$queue->bind('my-exchange');

消費者コード

$queue->consume(array($this, 'callback'));

public function callback(AMQPEnvelope $msg)
{
    try {
        //Do some business logic
    } catch (Exception $ex) {
        //Log exception
        return;
    }
    return $queue->ack($msg->getDeliveryTag());
}

プロデューサーコード

$exchange->publish('message');
14
Bram Gerritsen

メッセージが確認されず、アプリケーションが失敗した場合、メッセージは自動的に再配信され、エンベロープのredeliveredプロパティはtrueに設定されます(_no-ack = true_フラグでメッセージを消費しない限り)。

UPD:

キャッチブロックに再配信フラグを付けてnackメッセージを送信する必要があります

_    try {
        //Do some business logic
    } catch (Exception $ex) {
        //Log exception
        return $queue->nack($msg->getDeliveryTag(), AMQP_REQUEUE);
    }
_

RabbitMQおよびAMQPプロトコルでは再配信カウントがまったく実装されていない間、無限にナックされたメッセージに注意してください。

このようなメッセージをいじりたくなく、単に遅延を追加したい場合は、nackメソッド呼び出しの前にsleep()またはusleep()を追加することをお勧めします。それはまったく良い考えではありません。

サイクル再配信の問題に対処するための複数の手法があります。

1。依存 デッドレター交換

  • 長所:信頼性が高く、標準的で、明確
  • 短所:追加のロジックが必要

2。使用 メッセージごとまたはキューごとのTTL

  • 長所:実装が簡単で、標準であり、明確です
  • 短所:キューが長いと、メッセージが失われる可能性があります

例(キューttlの場合は数値のみを渡し、メッセージttlの場合は数値文字列になるものすべてを渡すことに注意してください):

2.1メッセージごとのttl:

_$queue = new AMQPQueue($channel);
$queue->setName('my-queue');
$queue->declareQueue();
$queue->bind('my-exchange');

$exchange->publish(
    'message at ' . microtime(true),
    null,
    AMQP_NOPARAM,
    array(
        'expiration' => '1000'
    )
);
_

2.2。キューごとのttl:

_$queue = new AMQPQueue($channel);
$queue->setName('my-queue');
$queue->setArgument('x-message-ttl', 1000);
$queue->declareQueue();
$queue->bind('my-exchange');

$exchange->publish('message at ' . microtime(true));
_

3。メッセージ本文またはヘッダーに再配信数または左再配信数(IPスタックのホップ制限またはttl)を保持します

  • 長所:アプリケーションレベルでメッセージの有効期間をさらに制御できます
  • 短所:メッセージを変更して再度公開する必要がある場合の重大なオーバーヘッド、アプリケーション固有、明確ではない

コード:

_$queue = new AMQPQueue($channel);
$queue->setName('my-queue');
$queue->declareQueue();
$queue->bind('my-exchange');

$exchange->publish(
    'message at ' . microtime(true),
    null,
    AMQP_NOPARAM,
    array(
        'headers' => array(
            'ttl' => 100
        )
    )
);

$queue->consume(
    function (AMQPEnvelope $msg, AMQPQueue $queue) use ($exchange) {
        $headers = $msg->getHeaders();
        echo $msg->isRedelivery() ? 'redelivered' : 'Origin', ' ';
        echo $msg->getDeliveryTag(), ' ';
        echo isset($headers['ttl']) ? $headers['ttl'] : 'no ttl' , ' ';
        echo $msg->getBody(), PHP_EOL;

        try {
            //Do some business logic
            throw new Exception('business logic failed');
        } catch (Exception $ex) {
            //Log exception
            if (isset($headers['ttl'])) {
                // with ttl logic

                if ($headers['ttl'] > 0) {
                    $headers['ttl']--;

                    $exchange->publish($msg->getBody(), $msg->getRoutingKey(), AMQP_NOPARAM, array('headers' => $headers));
                }

                return $queue->ack($msg->getDeliveryTag());
            } else {
                // without ttl logic
                return $queue->nack($msg->getDeliveryTag(), AMQP_REQUEUE); // or drop it without requeue
            }

        }

        return $queue->ack($msg->getDeliveryTag());
    }
);
_

メッセージの再配信フローをより適切に制御する方法は他にもいくつかあります。

結論:特効薬の解決策はありません。どのソリューションが自分のニーズに最適かを判断するか、他の何かを見つける必要がありますが、ここで共有することを忘れないでください;)

19
pinepain

コンシューマーを再起動したくない場合は、basic.recoverAMQPコマンドはあなたが望むものかもしれません。 AMQPプロトコル によると:

basic.recover(bit requeue)

Redeliver unacknowledged messages.

This method asks the server to redeliver all unacknowledged messages on a specified channel. 
Zero or more messages may be redelivered. This method replaces the asynchronous Recover. 
0
mike