ps:記事の遅延キュー=遅延キュー
遅延キューとは
遅延キューに格納されているオブジェクトは、対応する遅延メッセージである必要があります。いわゆる「遅延メッセージ」とは、メッセージが送信されたときに、消費者がメッセージをすぐに取得したくないが、指定された時間待機してから、これを取得することを意味します。メッセージは消費されます。
シナリオ1:注文システムでは、ユーザーは通常、注文後30分以内に支払う必要があります。30分以内に支払いが成功しなかった場合、注文は処理されます。これは、遅延キューを使用して注文情報を遅延キューに送信するためのものです。
シナリオ2:ユーザーは、自宅のスマートデバイスを携帯電話でリモート制御して、指定した時間に作業したいと考えています。このとき、ユーザー命令を遅延キューに送信することができ、命令で設定された時間が経過すると、命令はスマートデバイスにプッシュされます。
RabbitMQがレイトキューを実装する方法
方法1
AMQPプロトコルとRabbitMQキュー自体は、遅延キュー機能を直接サポートしていませんが、遅延キューの機能は、次の機能によってシミュレートできます。
ただし、RabbitMQの2つの特性を使用して、遅延キューをカーブさせることができます。
- 物理1、存続時間(TTL)
RabbitMQは、キューのx-expiresまたはメッセージのx-message-ttlを設定して、メッセージの存続期間を制御できます。タイムアウトすると(2つが同時に設定され、最初の有効期限が優先されます)、メッセージはデッドレターになります(デッドレター)
RabbitMQには、キュー内のメッセージの有効期限を設定する2つの方法があります。
- A:キューのプロパティ設定により、キュー内のすべてのメッセージの有効期限は同じになります。
- B:メッセージを個別に設定します。各メッセージのTTLは異なる場合があります。
同時に使用する場合、メッセージの有効期限は、2つの間の小さいTTLに基づきます。キュー内のメッセージの有効期間が設定されたTTL値を超えると、デッドレターになります
RabbitMQのキューは、x-dead-letter-exchangeとx-dead-letter-routing-key(オプション)の2つのパラメーターを使用して構成できます。デッドレターがキューに表示されると、これら2つのパラメーターに従って再ルーティングされ、指定されたものに転送されます。キュー。
- x-dead-letter-exchange:デッドレターが表示された後、指定されたエクスチェンジにデッドレターを再送信します
- x-dead-letter-routing-key:デッドレターが表示された後、指定されたルーティングキーに従ってデッドレターが再送信されます
キュー内のデッドレターには次のものが含まれます。
- メッセージまたはキューのTTLが期限切れになりました
- キューが最大長に達する
- メッセージはコンシューマー(basic.rejectまたはbasic.nack)によって拒否され、requeue = false
上記の2つの機能を組み合わせて、TTLルールを設定した後、メッセージがキュー内のデッドレターになった場合、DLX機能を使用して別のExchangeまたはルーティングキーに再転送し、メッセージを再度使用できます。
設定方法:
ステップ1:デッドレターを生成するようにTTLを設定します。メッセージごとのTTLとキューTTLの2つの方法があります。1つ目は各メッセージの有効期限を設定でき、ほとんどのシナリオで使用できます。2つ目は、キューの有効期限を設定して適用する方法です。 1回限りの遅延タスクの場合
basic.rejectまたはbasic.nackの消費を拒否する消費者など、デッドレターを生成する方法は他にもあります(消費者の属性requeue = falseの場合)
- メッセージごとのTTL(各メッセージの有効期限を設定)(公式ドキュメント)
Javaクライアントは、60秒間しか留まらないメッセージをキューに送信します。
byte[] messageBodyBytes = "Hello, world!".getBytes();
AMQP.BasicProperties properties = new AMQP.BasicProperties();
properties.setExpiration("60000");//设置消息的过期时间为60秒
channel.basicPublish("my-exchange", "routing-key", properties, messageBodyBytes);
//这条消息发送到相应的队列之后,如果60秒内没有被消费,则变为死信
- キューTTL(キュー全体の有効期限を設定)
キューを作成します。キューのメッセージ有効期限は30分です(30分以内に消費者消費メッセージがない場合、キューは削除され、キュー内のメッセージは削除後にデッドレターになります)
java client方式:
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-expires", 1800000);
channel.queueDeclare("myqueue", false, false, false, args);
rabbitmqctl命令方式(.* 为所有队列, 可以替换为指定队列):
rabbitmqctl set_policy expiry ".*" '{"expires":1800000}' --apply-to queues
rabbitmqctl (Windows):
rabbitmqctl set_policy expiry ".*" "{""expires"":1800000}" --apply-to queues
ステップ2:デッドレターの転送ルールを設定します(ルールがない場合は、デッドレターを直接破棄します)
- デッドレターエクスチェンジの設定方法(公式文書)
Java Client方式:
//声明一个直连模式的exchange
channel.exchangeDeclare("some.exchange.name", "direct");
//声明一个队列,当myqueue队列中有死信产生时,会转发到交换器some.exchange.name
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "some.exchange.name");
//如果设置死信会以路由键some-routing-key转发到some.exchange.name,如果没设默认为消息发送到本队列时用的routing key
//args.put("x-dead-letter-routing-key", "some-routing-key");
channel.queueDeclare("myqueue", false, false, false, args);
命令行方式(.* 为所有队列, 可以替换为指定队列):
设置 "dead-letter-exchange"
rabbitmqctl:
rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues
rabbitmqctl (Windows):
rabbitmqctl set_policy DLX ".*" "{""dead-letter-exchange"":""my-dlx""}" --apply-to queues
设置 "dead-letter-routing-key"
rabbitmqctl:
rabbitmqctl set_policy DLX ".*" '{ "dead-letter-routing-key":"my-routing-key"}' --apply-to queues
rabbitmqctl (Windows):
rabbitmqctl set_policy DLX ".*" "{""dead-letter-routing-key"":""my-routing-key""}" --apply-to queues
方法2
Rabbitmq 3.5.7以降は、遅延キュー機能を実装するためのプラグイン(rabbitmq-delayed-message-exchange)を提供します。同時に、プラグインはErlang / OPT18.0以降に依存しています。
プラグインのソースコードアドレス:https:
//github.com/rabbitmq/rabbitmq-delayed-message-exchange
プラグインのダウンロードリンク:https:
//bintray.com/rabbitmq/community-plugins/rabbitmq_delayed_message_exchange
インストール:
プラグインのインストールディレクトリ
{rabbitmq-server} / plugins /(既存のプラグインを確認できます)を入力して
、プラグインをダウンロードします
rabbitmq_delayed_message_exchange
wget https://bintray.com/rabbitmq/community-plugins/download_file?file_path=rabbitmq_delayed_message_exchange-0.0.1.ez
- 1
(ダウンロードしたファイル名が不規則な場合は、
rabbitmq_delayed_message_exchange-0.0.1.ezのように手動で名前を変更します)
プラグインを有効にする
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
(关闭插件)
rabbitmq-plugins disable rabbitmq_delayed_message_exchange
プラグインの使用
タイプ
x-delayed-messageの交換を宣言して、遅延メッセージング機能を使用します。x-delayed-messageは、rabbitmq自体ではなく、プラグインによって提供されるタイプです。
// ... elided code ...
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("my-exchange", "x-delayed-message", true, false, args);
// ... more code ...
メッセージを送信するときは、ヘッダーに「x-delay」パラメーターを追加して、メッセージの遅延時間を制御します
// ... elided code ...
byte[] messageBodyBytes = "delayed payload".getBytes("UTF-8");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 5000);
AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
channel.basicPublish("my-exchange", "", props.build(), messageBodyBytes);
// ... more code ...
使用例:
メッセージ送信者:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Send {
// 队列名称
private final static String EXCHANGE_NAME="delay_exchange";
private final static String ROUTING_KEY="key_delay";
@SuppressWarnings("deprecation")
public static void main(String[] argv) throws Exception {
/**
* 创建连接连接到MabbitMQ
*/
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.12.190");
factory.setUsername("admin");
factory.setPassword("admin");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// 声明x-delayed-type类型的exchange
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare(EXCHANGE_NAME, "x-delayed-message", true,
false, args);
Map<String, Object> headers = new HashMap<String, Object>();
//设置在2016/11/04,16:45:12向消费端推送本条消息
Date now = new Date();
Date timeToPublish = new Date("2016/11/04,16:45:12");
String readyToPushContent = "publish at " + sf.format(now)
+ " \t deliver at " + sf.format(timeToPublish);
headers.put("x-delay", timeToPublish.getTime() - now.getTime());
AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder()
.headers(headers);
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, props.build(),
readyToPushContent.getBytes());
// 关闭频道和连接
channel.close();
connection.close();
}
}
メッセージ受信者:
import java.text.SimpleDateFormat;
import java.util.Date;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
public class Recv {
// 队列名称
private final static String QUEUE_NAME = "delay_queue";
private final static String EXCHANGE_NAME="delay_exchange";
public static void main(String[] argv) throws Exception,
java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.12.190");
factory.setUsername("admin");
factory.setPassword("admin");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.queueDeclare(QUEUE_NAME, true,false,false,null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
channel.basicConsume(QUEUE_NAME, true, queueingConsumer);
SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
System.out.println("****************WAIT***************");
while(true){
QueueingConsumer.Delivery delivery = queueingConsumer
.nextDelivery(); //
String message = (new String(delivery.getBody()));
System.out.println("message:"+message);
System.out.println("now:\t"+sf.format(new Date()));
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
受信側を開始し、送信側を開始します。
操作結果:
****************WAIT***************
message:publish at 2016-11-04 16:44:16.887 deliver at 2016-11-04 16:45:12.000
now: 2016-11-04 16:45:12.023
結果は、2016-11-04 16:45:12.023にメッセージを受信し、2016-11-04 16:45:12.023を設定した時間から23ミリ秒の遅延があることを示しています。
注:rabbitmq-delayed-message-exchangeプラグインを使用しているときにキューに送信されるメッセージの数は、Web管理インターフェイスに表示されない場合があり、通常の機能の使用には影響しません。
注:使用中に、rabbitmq-delayed-message-exchangeプラグインを備えたRAMノードを有効にすると、再起動時に起動できないことが発見されました。ログを確認すると、タイムアウト例外が見つかりました。開発者は、これが起動プロセス中のノードであると説明しました。クラスタ関連のデータを同期すると起動タイムアウトが発生するため、Ramノードを使用しないことをお勧めします
插件開プラグイン:
RAMノードは空白で始まり、テーブルを同期するためのディスクノードが必要です。この場合、タイムアウトになります。
さらに重要なことに、RAMノードは必要ありません。99%のユーザーがそうではないので、そうするかどうかわからない場合は、確かにそうしません。