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');
メッセージが確認されず、アプリケーションが失敗した場合、メッセージは自動的に再配信され、エンベロープの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());
}
);
_
メッセージの再配信フローをより適切に制御する方法は他にもいくつかあります。
結論:特効薬の解決策はありません。どのソリューションが自分のニーズに最適かを判断するか、他の何かを見つける必要がありますが、ここで共有することを忘れないでください;)
コンシューマーを再起動したくない場合は、basic.recover
AMQPコマンドはあなたが望むものかもしれません。 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.