目次
アドバンテージ
-
高可用性: RabbitMQ は、高可用性を実現し、システムの安定した動作を保証するために、クラスターやミラーリングされたキューなどの複数の方法をサポートします。
-
強力な信頼性: RabbitMQ は、メッセージ配信の標準として AMQP プロトコルを使用しており、メッセージ配信の信頼性と順序性を保証できます。
-
強力な柔軟性: RabbitMQ は、ポイントツーポイント、パブリッシュ/サブスクライブ、ルーティング、RPC などの複数のメッセージ モードをサポートしており、ビジネス ニーズに応じて適切なモードを選択できます。
-
優れたパフォーマンス: RabbitMQ は Erlang 言語に基づいて開発されており、高い同時実行性と低い遅延を特徴としており、大量のデータとメッセージの高速処理をサポートします。
-
導入と管理が簡単: RabbitMQ は、構成、監視、管理を容易にするための豊富な管理ツールと API インターフェイスを提供します。同時に、操作をより簡単かつ理解しやすくするためのビジュアルインターフェイスも提供します。
-
オープンソースで無料: RabbitMQ はオープンソース ソフトウェアであり、完全に無料で使用できます。あらゆる環境に簡単に導入して使用できます。
欠点がある
-
学習コストが高い: RabbitMQ には多くの概念とテクノロジが含まれており、初心者にとっては学習が難しい場合があります。
-
複雑な構成: RabbitMQ の構成は比較的複雑であり、実際のニーズに応じて対応する設定を行う必要があります。構成が正しくないと、システムのパフォーマンスと安定性に影響が出る可能性があります。
-
リソース使用率が高い: RabbitMQ は Erlang 言語を使用して開発されており、多くのメモリと CPU リソースを必要とするため、低構成のサーバーにデプロイすると、システムの遅延やその他の問題が発生する可能性があります。
-
単一障害点: RabbitMQ はクラスター モードをサポートしていますが、場合によっては依然として単一障害点が存在します。
-
メッセージの蓄積: コンシューマのメッセージ処理が遅すぎる場合、または異常な状態が発生した場合、RabbitMQ はメッセージの蓄積やメモリ オーバーフローなどの問題を引き起こす可能性があります。
1. 電流制限
いわゆる現在の制限とは、コンシューマーが一度に消費できるメッセージの数を制限することです。
// 每次只消费1000条消息,RabbitMQ放过来的这1000条消息没有处理完,就不会再放进来新的消息
$channel->basic_qos(null, 1000, null);
この設定は通常は使用されませんが、メッセージ バックログが深刻な場合に使用されます。メッセージ バックログが深刻な場合にこの値が設定されていない場合、コンシューマがハングアップする可能性があります。
2.リターン機構
返されるリスナーは、一部のルーティング不可能なメッセージを処理するために使用されます。
プロデューサはエクスチェンジとルーティングキーを指定してメッセージを特定のキューに送信し、コンシューマはそのキューをリッスンして消費処理を行います。ただし、場合によっては、現在の交換が存在しない場合、またはメッセージの送信時に指定されたルーティングキーをルーティングできない場合があります。現時点で、そのような到達不能なメッセージをリッスンしたい場合は、リターン リスナーを使用する必要があります。
# 虽然定义了交换机,但是指定了一个没有绑定队列的路由键
<?php
declare(strict_types = 1);
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Exchange\AMQPExchangeType;
require __DIR__ . '/../vendor/autoload.php';
try {
$connection = new AMQPStreamConnection('192.168.78.200', 5672, 'root', '123456');
$channel = $connection->channel();
// 定义一个交换机
$channel->exchange_declare('test_return_exchange', AMQPExchangeType::DIRECT);
$message = new AMQPMessage("Hello World!");
// 这里我们指定了一个没有绑定队列的路由键,会导致rabbitmq找到不队列
$channel->basic_publish($message, 'test_return_exchange', 'xxxxxx');
while (true) {
$channel->wait();
}
$channel->close();
$connection->close();
} catch (Exception $e) {
print_r($e->getMessage());
}
コードは正常に実行され、宣言したスイッチが作成されましたが、指定したルーティング キーにバインドされたキューが見つからなかったため、メッセージは Rabbitmq にプッシュされませんでしたが、rabbitmq がエラーを報告しなかったため、プッシュが成功したと誤解されます。
プッシュ状態を監視するために確認機構を使用しても、スイッチが見つからなかったり、ルートがキューに到達できないため、確認は監視できません。ここでは、rabbitmq のもう 1 つの高度な機能である return を使用することしかできません。次のコードは、戻りメカニズムを導入しています。
<?php
declare(strict_types = 1);
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Exchange\AMQPExchangeType;
require __DIR__ . '/../vendor/autoload.php';
try {
$connection = new AMQPStreamConnection('192.168.78.200', 5672, 'root', '123456');
$channel = $connection->channel();
// 定义一个交换机
$channel->exchange_declare('test_return_exchange', AMQPExchangeType::DIRECT);
$message = new AMQPMessage("Hello World!");
// 指定第三个参数mandatory为true,表示当rabbitmq无法找到路由或队列时,将消息返回给生产者
// 这里我们指定了一个没有绑定队列的路由键,会导致rabbitmq找到不队列,进而触发return
$channel->basic_publish($message, 'test_return_exchange', 'xxxxxx', true);
// 监听消息未找到交换机或者未找到路由键对应的路由
$channel->set_return_listener(function ($replyCode, $replyText, $exchange, $routingKey, $message) {
$msg = 'oh hoo!发生错误了'.PHP_EOL;
$msg .= '错误码:'.$replyCode.PHP_EOL;
$msg .= '错误信息:'.$replyText.PHP_EOL;
$msg .= '指定的交换机:'.$exchange.PHP_EOL;
$msg .= '指定的路由键:'.$routingKey.PHP_EOL;
$msg .= '投递的消息:'.$message->body.PHP_EOL;
print_r($msg);
});
while (true) {
$channel->wait();
}
$channel->close();
$connection->close();
} catch (Exception $e) {
print_r($e->getMessage());
}
このとき、再度プログラムを実行するとエラーが報告されますが、ここではエラーを出力していますが、実際の運用では、指定したログデータテーブルにエラーが記録されるはずです。
# 程序返回
root@204d5054860c:/data/test-yii/web# php testReturn.php
oh hoo!发生错误了
错误码:312
错误信息:NO_ROUTE
指定的交换机:test_return_exchange
指定的路由键:xxxxxx
投递的消息:Hello World!
3. デッドレターキュー
デッド レター キューは、実際にはデッド レター スイッチにバインドされた通常のキューであり、デッド レター スイッチは単なる通常のスイッチですが、デッド レターを処理するために特別に使用されるスイッチです。
デッドレター メッセージは、RabbitMQ が保証するレイヤーです。実際、デッドレター キューを使用することもできませんが、メッセージの消費が異常な場合、メッセージは別のスイッチにアクティブに配信されるか、ログ テーブルに記録されます。その後の特殊加工用です。
以下のシナリオでは、メッセージは Rabbitmq によってデッドレターと判断され、デッドレターキューに入れられます。
-
メッセージが拒否されました
-
メッセージの有効期限が切れました
-
キューの過負荷
次のシミュレーションは、メッセージが通常のキューに入れられることを示しています。このメッセージの有効期限は 10 秒に設定されています。10 秒以内にメッセージを処理するコンシューマがないため、このメッセージは期限切れになり、デッドレターになります。この時点では、メッセージは期限切れになり、デッドレターになります。 、RabbitMQ これは、コード内で宣言したデッドレター キューであるデッドレター キューに入れられます。
注: Rabbitmq にはデッド レター キューが 1 つだけではなく、複数のデッド レター キューが存在する可能性があります。デッド レター キューを指定しない場合、期限切れのメッセージは Rabbitmq によって破棄されます。
<?php
declare(strict_types = 1);
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Exchange\AMQPExchangeType;
use PhpAmqpLib\Wire\AMQPTable;
require __DIR__ . '/../vendor/autoload.php';
try {
$connection = new AMQPStreamConnection('192.168.78.200', 5672, 'root', '123456');
// 设置交换机名称
$deadLetterExchangeNameName = 'dead_letter_exchange';
$normalExchangeName = 'exchange_6';
// 设置队列名称
$deadLetterQueueName = 'dead_letter_queue';
$normalQueueName = 'queue_6';
// 设置路由键
$deadLetterRoutingKey = 'dead_letter_key';
$normalRoutingKey = 'route_6';
// 创建通道
$channel = $connection->channel();
// 声明交换器,作为死信交换器
$channel->exchange_declare($deadLetterExchangeNameName, AMQPExchangeType::DIRECT, false, true);
// 声明交换器,作为普通交换器
$channel->exchange_declare($normalExchangeName, AMQPExchangeType::DIRECT, false, true);
$args = new AMQPTable();
// 设置消息过期时间为25s,25秒后消息会进入死信队列
$args->set('x-message-ttl', 25000);
// 设置死信交换器
$args->set('x-dead-letter-exchange', $deadLetterExchangeNameName);
// 设置死信路由键
$args->set('x-dead-letter-routing-key', $deadLetterRoutingKey);
// 声明队列,作为普通队列,同时将死信队列相关参数传入
$channel->queue_declare($normalQueueName, false, true, false, false, false, $args);
// 声明死信队列
$channel->queue_declare($deadLetterQueueName, false, true, false, false);
// 将普通队列与普通交换机绑定
$channel->queue_bind($normalQueueName, $normalExchangeName, $normalRoutingKey);
// 将死信队列与死信交换机绑定,同时指定死信路由键
$channel->queue_bind($deadLetterQueueName, $deadLetterExchangeNameName, $deadLetterRoutingKey);
// 设置传递的消息
$message = new AMQPMessage('Hello DLX Message queue_6');
// confirm监听消息投递【与死信队列无关,仅用于排错】
$channel->confirm_select();
$channel->set_ack_handler(function ($message) {
print_r('投递成功啦,消息是'.$message->body);
}) ;
$channel->set_nack_handler(function ($message) {
print_r('投递失败啦,消息是'.$message->body);
}) ;
// return监听未找到交换机或路由问题【与死信队列无关,仅用于排错】
$channel->set_return_listener(function ($replyCode, $replyText, $exchange, $routingKey, $message) {
$msg = 'oh hoo!发生错误了'.PHP_EOL;
$msg .= '错误码:'.$replyCode.PHP_EOL;
$msg .= '错误信息:'.$replyText.PHP_EOL;
$msg .= '指定的交换机:'.$exchange.PHP_EOL;
$msg .= '指定的路由键:'.$routingKey.PHP_EOL;
$msg .= '投递的消息:'.$message->body.PHP_EOL;
print_r($msg);
});
// 发送消息到RabbitMQ
$channel->basic_publish($message, $normalExchangeName, $normalRoutingKey, true);
while (true) {
$channel->wait();
}
// 关闭通道和连接
$channel->close();
$connection->close();
} catch (Exception $e) {
print_r($e->getMessage());
}