RabbitMQ のメッセージ確認メカニズムとは何ですか? なぜメッセージの確認が必要なのでしょうか?
RabbitMQ のメッセージ確認メカニズムとは、プロデューサーがメッセージを送信した後、メッセージが正しく受信され、処理されたことをコンシューマーが確認するのを待つメカニズムを指します。メッセージ確認メカニズムの主な目的は、メッセージの信頼性の高い配信と処理を保証し、メッセージの損失や処理の繰り返しを回避することです。
なぜメッセージ確認メカニズムが必要なのでしょうか? 分散システムでは、メッセージの送受信は非同期プロセスであり、次の状況が存在する可能性があります。
-
メッセージ損失: メッセージ送信プロセス中に、ネットワーク障害、ハードウェア障害、またはその他の理由によりメッセージが失われる可能性があります。メッセージ確認メカニズムがない場合、プロデューサーはメッセージがコンシューマーに正常に配信されたかどうかを知ることができないため、メッセージの信頼性を保証できません。
-
メッセージの重複: メッセージ送信プロセス中に、ネットワークのタイムアウト、コンシューマの障害、またはその他の理由により、メッセージが繰り返し送信される可能性があります。メッセージ確認メカニズムがないと、コンシューマーは同じメッセージを複数回処理する可能性があり、その結果、操作が繰り返され、データの不整合が発生します。
上記の問題を解決するために、RabbitMQ はメッセージ確認メカニズムを導入しました。メッセージ確認メカニズムには、公開確認と消費者確認という 2 つの重要な概念が含まれています。
パブリッシュ確認とは、プロデューサーがメッセージを送信した後、RabbitMQ サーバーが確認メッセージを返すのを待つプロセスを指します。プロデューサはchannel.confirmSelect()
メソッドを呼び出して公開確認モードを有効にし、channel.waitForConfirms()
そのメソッドを使用してサーバーから確認メッセージが返されるのを待つことができます。サーバーがメッセージを正常に受信して処理すると、確認メッセージがプロデューサーに返されます。
以下は、メッセージ確認メカニズムの使用方法を示す Java で書かれたコード例です。
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConfirmListener;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;
public class MessageConfirmationExample {
private final static String QUEUE_NAME = "my_queue";
private final static String MESSAGE = "Hello, RabbitMQ!";
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// 启用发布确认模式
channel.confirmSelect();
// 创建一个有序集合,用于保存未确认的消息的Delivery Tag
SortedSet<Long> unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet<>());
// 添加发布确认监听器
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if (multiple) {
// 多条消息被确认,移除所有小于等于deliveryTag的消息
unconfirmedSet.headSet(deliveryTag + 1).clear();
} else {
// 单条消息被确认,移除该消息
unconfirmedSet.remove(deliveryTag);
}
System.out.println("Message confirmed: " + deliveryTag);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if (multiple) {
// 多条消息未被确认,重新发送所有小于等于deliveryTag的消息
unconfirmedSet.headSet(deliveryTag + 1).forEach(tag -> {
try {
sendMessage(channel, tag);
} catch (IOException e) {
e.printStackTrace();
}
});
} else {
// 单条消息未被确认,重新发送该消息
sendMessage(channel, deliveryTag);
}
System.out.println("Message not confirmed: " + deliveryTag);
}
});
// 发送消息
for (int i = 1; i <= 10; i++) {
long deliveryTag = sendMessage(channel, i);
unconfirmedSet.add(deliveryTag);
}
// 等待所有消息被确认
try {
channel.waitForConfirmsOrDie();
System.out.println("All messages confirmed!");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 关闭通道和连接
channel.close();
connection.close();
}
private static long sendMessage(Channel channel, int messageNumber) throws IOException {
long deliveryTag = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, (MESSAGE + " " + messageNumber).getBytes());
System.out.println("Sent message: " + messageNumber);
return deliveryTag;
}
}
上記のコードでは、まず接続ファクトリーを作成し、RabbitMQ サーバーのホスト アドレスを設定します。次に、接続ファクトリーを使用して接続を作成し、その接続を使用してチャネルを作成しました。次に、「my_queue」という名前のキューを宣言します。channel.confirmSelect()
次に、メソッドを呼び出してリリース確認モードを有効にしました。同時に、unconfirmedSet
未確認メッセージの配信タグを保存するための順序付きコレクションを作成しました。
次に、リリース確認リスナーを追加しConfirmListener
、リスナーにhandleAck
とメソッドを実装しました。handleNack
メッセージが確認されると、handleAck
メソッドが呼び出され、このメソッドでは、確認されたメッセージをunconfirmedSet
メッセージから。このメソッドは、メッセージが確認応答されないときに呼び出されhandleNack
、確認応答されていないメッセージの再送信など、このメソッドで確認応答されていないロジックを処理できます。
次に、sendMessage
メソッドを使用して 10 個のメッセージを送信し、各メッセージの配信タグを に保存しましたunconfirmedSet
。次に、channel.waitForConfirmsOrDie()
メソッドを使用して、すべてのメッセージが確認されるまで待機します。すべてのメッセージに対する確認応答が指定された時間内に受信されない場合、InterruptedException
例外がスローされます。