序文
前のメッセージング・ミドルウェア--RabbitMQ(7)すべてここに高度な機能!(上)私たちは、導入消息如何保障100%的投递成功?
、幂等性概念详解
、在海量订单产生的业务高峰期,如何避免消息的重复消费的问题?
、をConfirm确认消息、Return返回消息
。これは、我々は次の内容を導入する必要があります。
- カスタム消費者
- (あまりにも多くのメモリを防ぐために、ノードがダウンした)メッセージフローリストリクタ
- ACKメッセージキューと復帰
- TTLのニュース
- デッドレターキュー
1.カスタム消費者
1.1消費者側のカスタムモニター
我々は通常、単にループ、consumer.nextDelivery方法は、次のメッセージを得るためにしながら、コードを書き、その後、消費のために処理します!
しかし、これは確かに、コードが比較的低い、仕方回転で良いではありません。
- 私たちは、それが実用的な作品の中で最も一般的に使用され、カスタムの消費者より便利に、より強力なデカップリングを使用します!
1.2コードショー
1.2.1生産
/**
*
* @ClassName: Producer
* @Description: 生产者
* @author Coder编程
* @date2019年7月30日 下午23:15:51
*
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = "test_consumer_exchange";
String routingKey = "consumer.save";
String msg = "Hello RabbitMQ Consumer Message";
for(int i =0; i<5; i ++){
channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
}
}
}
复制代码
1.2.2消費者
/**
*
* @ClassName: Consumer
* @Description: 消费者
* @author Coder编程
* @date2019年7月30日 下午23:13:51
*
*/
public class Consumer {
public static void main(String[] args) throws Exception {
// 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_consumer_exchange";
String routingKey = "consumer.#";
String queueName = "test_consumer_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//实现自己的MyConsumer()
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
复制代码
1.2.3カスタムクラス:MyConsumer
/**
*
* @ClassName: MyConsumer
* @Description: TODO
* @author Coder编程
* @date 2019年7月30日 下午23:11:55
*
*/
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
//根据需求,重写自己需要的方法。
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
//消费标签
System.err.println("consumerTag: " + consumerTag);
//这个对象包含许多关键信息
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
}
}
复制代码
1.3印刷結果
2.消費者側を制限します
2.1何が消費者側を制限していますか?
- シーンを想定、すべての最初の、我々は1万上のRabbitMQサーバ未処理のメッセージを持って、私たちは消費者のクライアントを開いて、以下の条件を表示されます。
- インスタントメッセージの膨大な量は、すべての上にプッシュするが、我々は、単一のクライアントが同時にので、多くのデータを扱うことができない持っています!この時間は、失敗し、サーバーがクラッシュすることは容易です。
なぜそれの生産面を限定することではありませんか?
高い同時実行の場合には、顧客のボリュームが非常に大きいので、生産制限の最後に行うことは困難です。だから我々は、消費者側の制限MQで行うことができます。
- RabbitMQのは、QoS(サービス品質)機能を提供するには、メッセージの特定の数は以前に確認されない場合という前提で非自動受信確認メッセージであり、新しいニュースの消費量ではありません(値ベースのチャンネルセットによってまたはQoSを消費します) 。電流制限の場合は、手動看板に設定する、自動受信を設定しないでください。
- 空BasicQos(UINT prfetchSize、USHORT prefetchCount、グローバルなブール値);
パラメータの説明: PREFETCHSIZE:0 prefetchCount:一度Nがあるニュースは、その後、消費者がメッセージACKまでACKオフブロックされますされていないN個のメッセージよりも多くの消費者を、プッシュするのRabbitMQを教えないでください。グローバル:限度を超えている、簡単に言えば、チャンネルに上記の設定の場合はfalse \真のチャネルレベルや消費者レベルです。PREFETCHSIZEとグローバル2、RabbitMQの達成ではないが、力への研究prefetch_countのno_ask =虚偽の場合ではない当分の間、つまり、これら2つの値の場合には、自動的に強制ではありません答えます。
最初の引数:サイズ制限は、メッセージは、メガバイトの数のメッセージです。一般的に限定するものではなく、二番目のパラメータが0に設定されています:どのように多くの治療の最大値は、実際の作業は、第三引数のように1に設定されています:ポリシーの適用を制限しているもの。レベル1 2.Consumerチャネル:一般的に二つのアプリケーションのRabbitMQのレベルがあります。通常、偽の真の表現チャンネルレベル、消費者レベルでの偽の表現に設定
2.2コードショー
2.2.1生産
/**
*
* @ClassName: Producer
* @Description: 生产者
* @author Coder编程
* @date2019年7月30日 下午23:15:51
*
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = "test_qos_exchange";
String routingKey = "qos.save";
String msg = "Hello RabbitMQ QOS Message";
for(int i =0; i<5; i ++){
channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
}
}
}
复制代码
2.2.2消費者
/**
*
* @ClassName: Consumer
* @Description: 消费者
* @author Coder编程
* @date2019年7月30日 下午23:13:51
*
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_qos_exchange";
String queueName = "test_qos_queue";
String routingKey = "qos.#";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//1 限流方式 第一件事就是 autoAck设置为 false
//设置为1,表示一条一条数据处理
channel.basicQos(0, 1, false);
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
复制代码
2.2.3カスタムクラス:MyConsumer
/**
*
* @ClassName: MyConsumer
* @Description: TODO
* @author Coder编程
* @date 2019年7月30日 下午23:11:55
*
*/
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
//需要做签收,false表示不支持批量签收
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
复制代码
2.2.4テスト結果
コメントしてみましょう:channel.basicAck(envelope.getDeliveryTag()はfalse);その後、消費者を起動します。ビューの交換
表示キュー
その後、プロデューサーを開始します。ビューには、結果を印刷します:
私達はちょうどメッセージを受信し、消費者側にあります。これはなぜでしょうか?
私たちは、消費者にあるため、最初のポイント
channel.basicConsume(queueName, false, new MyConsumer(channel));
复制代码
二番目のパラメータを手動で署名するfalseに設定されています。
第二の点は、QoSがメッセージのみを受け入れるように配置されています。このメッセージは、ブローカーのAckに応じない場合、Brokerはこのメッセージコンシューマを行っていない、それはメッセージを送り続けないだろうと思います。
channel.basicQos(0, 1, false);
复制代码
制御局は、5 =合計、レディ= 4,1 =未確認、見ることができます。
次はchannel.basicAck(偽envelope.getDeliveryTagを()、)コメントを開く、メッセージの受信。サービスを再起動します。
3.1印刷結果
私たちは、通常の印刷5の結果を見ることができます4.消費者側とバックACKキュー
4.1消費者側の手ACKとNACK
消費者側が消費するとき、そしてもし我々が原因トラフィックログに例外を作成して、補正することができます!
サーバの障害に起因する深刻な問題は、我々は、手動で、消費者の最終消費者の成功を保護するためにACKする必要がある場合は!
バックキューに4.2消費者側
メッセージのキューの消費者側に戻るが正常に処理されていない、メッセージがブローカに再配信されます!
一般的に、我々は実用的なアプリケーションでは、Falseに設定されているキューに戻るには閉じられます。
4.3のコードに示します
4.3.1生産
/**
*
* @ClassName: Producer
* @Description: 生产者
* @author Coder编程
* @date2019年7月30日 下午23:15:51
*
*/
public class Producer {
public static void main(String[] args) throws Exception {
//1创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = "test_ack_exchange";
String routingKey = "ack.save";
for(int i =0; i<5; i ++){
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("num", i);
//添加属性,后续会使用到
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) //投递模式,持久化
.contentEncoding("UTF-8")
.headers(headers)
.build();
String msg = "Hello RabbitMQ ACK Message " + i;
channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
}
}
}
复制代码
4.3.2消費者
/**
*
* @ClassName: Consumer
* @Description: 消费者
* @author Coder编程
* @date2019年7月30日 下午23:13:51
*
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//1创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_ack_exchange";
String queueName = "test_ack_queue";
String routingKey = "ack.#";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
// 手工签收 必须要关闭 autoAck = false
channel.basicConsume(queueName, false, new MyConsumer(channel));
}
}
复制代码
4.3.3カスタムクラス:MyConsumer
/**
*
* @ClassName: MyConsumer
* @Description: TODO
* @author Coder编程
* @date 2019年7月30日 下午23:11:55
*
*/
public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("body: " + new String(body));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if((Integer)properties.getHeaders().get("num") == 0) {
//Nack三个参数 第二个参数:是否是批量,第三个参数:是否重回队列(需要注意可能会发生重复消费,造成死循环)
channel.basicNack(envelope.getDeliveryTag(), false, true);
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}
复制代码
5.1印刷結果:
注:戻り待ち行列は死の繰り返し消費サイクルで問題が発生します見ることができ、この時例えば、以上の3回の後、再試行の最高のセット数、メッセージや消費者が失敗したが、メッセージは破棄されます。
6. TTLキュー/メッセージ
6.1 TTL
- TTLライブまでの時間は、生存期間である略語であり、
- RabbitMQのは、メッセージを指定することができます送信された場合、メッセージの有効期限をサポートしています
- RabbitMQのは、キューからメッセージがタイムアウトキュー構成よりも、メッセージは自動的にクリアな限り、カウントキューの有効期限をサポートしています
6.2コードショー
制御ユニットによって直接プレゼンテーションを6.2.1
キュー制御ユニットによって作成されました10秒間の最大キューサイズのxメッセージ-TTLセットのX-MAX-長のメッセージが消費されていない場合は、その後、クリアされます。
交換を追加 キューとExchangeのバインドtest_ttl_exchangeバインドをクリックします
結合が成功したかどうかを確認するには メッセージ制御テーブルを送信することにより、 未処理のメッセージは自動的にクリアされます制作側は、有効期限を設定します
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.headers(headers)
.build();
复制代码
これら二つの特性は同じではない、それは、メッセージ本体、期限切れに対応するキューに対応しています。
7.不能キュー
7.1概念の理解
デッドレターキュー:DLXは、Exchangeデッドレター交換のRabbitMQは密接に関連しているデッドレターとチーム
- メッセージがキューにデッドレター(デッドメッセージ)になったとき、それは別のものに再パブリッシュ交換することができDLXの使用は、これはDLXの交換であります
状況を以下のニュースになって死んで手紙
- メッセージは拒否(basic.reject / basic.nack)と再キューイング= falseをされます
- TTLは、メッセージを期限切れ
- キューの最大長
DLXは、通常の取引所で取引所と一般的には違いはありません、それはどのキューに指定することができ、キューは、実際の属性のセットです
これはデッドレターキューを持っている場合は、RabbitMQのは自動的にExchangeにこのメッセージを再発行します設定し、その後、別のキューにルーティングすること。
適切な治療を行うには、キュー内のメッセージを聞くことができ、この機能は、以前サポートRabbitMQ3.0即時関数パラメータを行うことができます。
7.2コードショー
- デッドレターキューの設定:
- 交換を:まず、我々は結合し、その後、為替不能キュー、キューを設定する必要がありdlx.exchangeキュー:dlx.queue RoutingKey:#を
- その後、我々は、キュー、バインド、通常のスイッチを宣言し、私たちは、キューにパラメータを追加する必要があります。arguments.put(「X-デッドレター交換」、「dlx.exchange」);
- したがって、キューに再登録するには有効期限のメッセージ、キューの最大長は、メッセージが配信不能キューに直接ルーティングすることができます!
7.2.1生産
/**
*
* @ClassName: Producer
* @Description: 生产者
* @author Coder编程
* @date2019年7月30日 下午23:15:51
*
*/
public class Producer {
public static void main(String[] args) throws Exception {
//创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
String exchange = "test_dlx_exchange";
String routingKey = "dlx.save";
String msg = "Hello RabbitMQ DLX Message";
for(int i =0; i<1; i ++){
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.build();
channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
}
}
}
复制代码
7.2.2消費者
/**
*
* @ClassName: Consumer
* @Description: 消费者
* @author Coder编程
* @date2019年7月30日 下午23:13:51
*
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//创建ConnectionFactory
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
// 这就是一个普通的交换机 和 队列 以及路由
String exchangeName = "test_dlx_exchange";
String routingKey = "dlx.#";
String queueName = "test_dlx_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
Map<String, Object> agruments = new HashMap<String, Object>();
agruments.put("x-dead-letter-exchange", "dlx.exchange");
//这个agruments属性,要设置到声明队列上
channel.queueDeclare(queueName, true, false, false, agruments);
channel.queueBind(queueName, exchangeName, routingKey);
//要进行死信队列的声明:
channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
channel.queueDeclare("dlx.queue", true, false, false, null);
channel.queueBind("dlx.queue", "dlx.exchange", "#");
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
复制代码
7.2.3カスタムクラス:MyConsumer
/**
*
* @ClassName: MyConsumer
* @Description: TODO
* @author Coder编程
* @date 2019年7月30日 下午23:11:55
*
*/
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
}
}
复制代码
7.2.4テスト結果
ファイル名を指定して実行消費者、制御局ビュービュー交流を
ビューキュー これは、デッドレターキューdlx_queueにメッセージを送信し、不能キューが発生した場合を示すよりtest_dlx_queue DLX識別を見ることができます閉じる消費者、唯一ランニングプロデューサー
10秒以上後に、メッセージの有効期限
私たちの仕事では、デッドレターキューはデッドレター状態では、消費者ではないニュースのために非常に重要です。私たちは、補償機構を使用することができます。
概要
これは、RabbitMQの100%のメッセージ配信の成功と冪等、確認メッセージ、リターン・メッセージ、ACKを保護し、キューに戻る方法、RabbitMQのの高度な機能を紹介する最初の実際の使用で、インターネットの巨人を導入しましたメッセージ、およびタイムアウトの使用、配信不能キューを制限します
文末
個人的なマイクロチャネル公衆番号へようこそ注意:Coderのプログラムの最新オリジナルな技術に関する記事と無料の学習教材のため、多くのブティックマインドマップ、インタビューデータ、PMPの準備資料あなたがリードするために、待っているあなたは、いつでも、どこでも技術的な知識を習得することができます!QQのグループを作成します:315 211 365を、私たちは一緒にグループ交換研究に歓迎します。ありがとうございます!また、必要としている友人の側に導入することができます。
記事のGithubに含ま:github.com/CoderMerlin ... Gitee:gitee.com/573059382/c ...歓迎の注意と星〜
参考記事:
「簡潔RabbitMQのメッセージング・ミドルウェア」
推奨読書:
メッセージングミドルウェア--RabbitMQ(5)クイックスタート生産者と消費者、SpringBootはRabbitMQの統合します!