メッセージの転送、ロック、および解決Message transfers, locks, and settlement

Service Bus などのメッセージ ブローカーの重要な機能は、メッセージを受信し、後で取得できるようにキューまたはトピックに保存することです。The central capability of a message broker such as Service Bus is to accept messages into a queue or topic and hold them available for later retrieval. 送信という用語は、一般的に、メッセージ ブローカーにメッセージを転送することを指します。Send is the term that is commonly used for the transfer of a message into the message broker. 受信という用語は、一般的に、メッセージを取得しているクライアントへメッセージを転送することを指します。Receive is the term commonly used for the transfer of a message to a retrieving client.

クライアントがメッセージを送信する場合、通常は、メッセージが正常に転送され、ブローカーが受信したか、またはなんらかのエラーが発生したかどうかの確認が求められます。When a client sends a message, it usually wants to know whether the message has been properly transferred to and accepted by the broker or whether some sort of error occurred. この肯定または否定応答により、クライアントおよびブローカーは、メッセージの転送状態について理解し、これを解決と呼びます。This positive or negative acknowledgment settles the client and the broker understanding about the transfer state of the message and is thus referred to as settlement.

同様に、ブローカーがメッセージをクライアントに転送する場合、ブローカーとクライアントは、メッセージが正常に送信、処理され、削除できる状態か、またはメッセージの転送や処理が失敗し、メッセージを再送信する必要があるかの確認を求めます。Likewise, when the broker transfers a message to a client, the broker and client want to establish an understanding of whether the message has been successfully processed and can therefore be removed, or whether the message delivery or processing failed, and thus the message might have to be delivered again.

送信操作の解決Settling send operations

サポートされている Service Bus API クライアントを使用した Service Bus への送信操作では、通常、明示的に解決されます。API 操作は、Service Bus からの受信結果を受け取るまで待機してから送信操作を完了します。Using any of the supported Service Bus API clients, send operations into Service Bus are always explicitly settled, meaning that the API operation waits for an acceptance result from Service Bus to arrive, and then completes the send operation.

Service Bus によってメッセージが拒否された場合、拒否には、"追跡 ID" が含まれたテキストとエラーのインジケーターが含まれます。If the message is rejected by Service Bus, the rejection contains an error indicator and text with a "tracking-id" inside of it. 拒否には、操作が成功する可能性があり、操作を再試行するかどうかに関する情報も含まれています。The rejection also includes information about whether the operation can be retried with any expectation of success. この情報はクライアント内で例外に変換され、送信操作の呼び出し元に報告されます。In the client, this information is turned into an exception and raised to the caller of the send operation. メッセージが受け取られた場合、操作は自動的に完了します。If the message has been accepted, the operation silently completes.

.NET 標準クライアントおよび Java クライアント用の排他的なプロトコルであり、.NET Framework クライアントのオプション である AMQP プロトコルを使用する場合、メッセージの転送および解決はパイプライン化され完全に非同期で実行されるため、非同期プログラミング モデル API のバリエーションを使用することをお勧めします。When using the AMQP protocol, which is the exclusive protocol for the .NET Standard client and the Java client and which is an option for the .NET Framework client, message transfers and settlements are pipelined and completely asynchronous, and it is recommended that you use the asynchronous programming model API variants.

送信プログラムは、SBMP プロトコルまたは HTTP 1.1 を使用した場合のように、各メッセージの確認を待機する必要がなく、複数のメッセージを短時間に連続的に送信できます。A sender can put several messages on the wire in rapid succession without having to wait for each message to be acknowledged, as would otherwise be the case with the SBMP protocol or with HTTP 1.1. これらの非同期送信操作は、各メッセージの受け取りや、パーティション分割されたエンティティへのメッセージの格納と同時に、または別のエンティティへの送信と重複して実行できます。Those asynchronous send operations complete as the respective messages are accepted and stored, on partitioned entities or when send operation to different entities overlap. 元の送信順序とは異なる順序で完了する場合もあります。The completions might also occur out of the original send order.

送信操作の結果をどう処理するかを決める戦略は、アプリケーションのパフォーマンスに即時の、かつ重要な影響を与える可能性があります。The strategy for handling the outcome of send operations can have immediate and significant performance impact for your application. このセクションの例は C# で記述されていますが、Java Futures にも同様に適用されます。The examples in this section are written in C# and apply equivalently for Java Futures.

これ例では単純なループを使ってアプリケーションによって大量のメッセージが生成され、各送信操作が完了してから次のメッセージを送信しています。同期および非同期 API では、いずれも、解決するまで順次 10 回完全なラウンドトリップを行ってから 10 メッセージのみ送信されています。If the application produces bursts of messages, illustrated here with a plain loop, and were to await the completion of each send operation before sending the next message, synchronous or asynchronous API shapes alike, sending 10 messages only completes after 10 sequential full round trips for settlement.

あるオンプレミス サイトから Service Bus までの TCP ラウンドトリップ遅延距離を 70 ミリ秒と想定し、Service Bus が各メッセージを受け取り、保存するまで 10 ミリ秒しかかからないとしても、次のループは 8 秒以上かかり、これには、ペイロードの転送時間やルートの輻輳効果の可能性は考慮に入れられていません。With an assumed 70 millisecond TCP roundtrip latency distance from an on-premises site to Service Bus and giving just 10 ms for Service Bus to accept and store each message, the following loop takes up at least 8 seconds, not counting payload transfer time or potential route congestion effects:

for (int i = 0; i < 100; i++)
{
  // creating the message omitted for brevity
  await client.SendAsync(…);
}

アプリケーションが連続的に 10 個の非同期送信操作を開始し、各送信操作の完了を個別に待機した場合、これら 10 個の操作のラウンドトリップ時間は重複します。If the application starts the 10 asynchronous send operations in immediate succession and awaits their respective completion separately, the round trip time for those 10 send operations overlaps. 10 個のメッセージが連続して転送され、この場合 TCP フレームが同じである可能性もあり、転送時間は主に、ブローカーにメッセージを転送するためのネットワーク関係の時間によって決まります。The 10 messages are transferred in immediate succession, potentially even sharing TCP frames, and the overall transfer duration largely depends on the network-related time it takes to get the messages transferred to the broker.

前述のループでも同様に推定すると、その次のループの重複実行時間の合計は、1 秒をはるかに下回る可能性があります。Making the same assumptions as for the prior loop, the total overlapped execution time for the following loop might stay well under one second:

var tasks = new List<Task>();
for (int i = 0; i < 100; i++)
{
  tasks.Add(client.SendAsync(…));
}
await Task.WhenAll(tasks);

すべての非同期プログラミング モデルでは、保留中の操作を保持するため、メモリ ベースの隠された作業キューが使用されることに注意してください。It is important to note that all asynchronous programming models use some form of memory-based, hidden work queue that holds pending operations. SendAsync (C#) または Send (Java) が返ると、送信タスクは作業キューに入りますが、プロトコル動作は、タスクの実行順序がくるまで開始されません。When SendAsync (C#) or Send (Java) return, the send task is queued up in that work queue but the protocol gesture only commences once it is the task's turn to run. 信頼性が考慮されるべき状況で、メッセージを連続送信する傾向のあるコードの場合、実際に送信されるまですべての送信メッセージがメモリを使用するため、一度に送信されるメッセージが多くなりすぎないよう注意する必要があります。For code that tends to push bursts of messages and where reliability is a concern, care should be taken that not too many messages are put "in flight" at once, because all sent messages take up memory until they have factually been put onto the wire.

次のコード スニペットで示すように、C# のセマフォは、必要な時にアプリケーション レベルを調整できる同期オブジェクトです。Semaphores, as shown in the following code snippet in C#, are synchronization objects that enable such application-level throttling when needed. このようにセマフォを使用すると、同時に送信されるメッセージを最大 10 個に制限できます。This use of a semaphore allows for at most 10 messages to be in flight at once. 使用できる 10 個のセマフォ ロックの 1 つが送信前に取得され、送信が完了すると解放されます。One of the 10 available semaphore locks is taken before the send and it is released as the send completes. 11 番目のセマフォは、送信前の少なくとも 1 つが完了されるまでループで待機してから、ロックを使用できるようにします。The 11th pass through the loop waits until at least one of the prior sends has completed, and then makes its lock available:

var semaphore = new SemaphoreSlim(10);

var tasks = new List<Task>();
for (int i = 0; i < 100; i++)
{
  await semaphore.WaitAsync();

  tasks.Add(client.SendAsync(…).ContinueWith((t)=>semaphore.Release()));
}
await Task.WhenAll(tasks);

アプリケーションは、操作の結果を取得しない、"送信して放置" 方法で非同期送信する設計には絶対にしない必要がありますApplications should never initiate an asynchronous send operation in a "fire and forget" manner without retrieving the outcome of the operation. そのような設計では、内部および隠れたタスク キューがメモリを消費し、アプリケーションが送信エラーを検知できなくなります。Doing so can load the internal and invisible task queue up to memory exhaustion, and prevent the application from detecting send errors:

for (int i = 0; i < 100; i++)
{

  client.SendAsync(message); // DON’T DO THIS
}

低レベルの AMQP クライアントの場合、Service Bus では "解決済み" 転送を実行できます。With a low-level AMQP client, Service Bus also accepts "pre-settled" transfers. 解決済み転送とは、操作の結果が成功でも失敗でもクライアントに報告されず、メッセージは送信時点で解決したとみなされる、"送信して放置" 操作です。A pre-settled transfer is a fire-and-forget operation for which the outcome, either way, is not reported back to the client and the message is considered settled when sent. クライアントへのフィードバックがないため、診断に使用できる行動可能なデータがなく、このモードは Azure のサポートを受ける資格がありません。The lack of feedback to the client also means that there is no actionable data available for diagnostics, which means that this mode does not qualify for help via Azure support.

受信操作の解決Settling receive operations

受信操作を行う Service Bus API クライアントでは、明示的なモードが 2 つ有効になっています。受信して削除ピーク ロックです。For receive operations, the Service Bus API clients enable two different explicit modes: Receive-and-Delete and Peek-Lock.

受信して削除モードでは、ブローカーは、ブローカーが受信クライアントに送信するすべてのメッセージを、送信時点で解決済みとみなします。The Receive-and-Delete mode tells the broker to consider all messages it sends to the receiving client as settled when sent. つまり、ブローカーが送信するとただちにメッセージは消費されたとみなされます。That means that the message is considered consumed as soon as the broker has put it onto the wire. メッセージの転送が失敗した場合、メッセージは失われます。If the message transfer fails, the message is lost.

このモードのメリットは、受信側メッセージでそれ以上のアクションを実行する必要はなく、解決の結果を待機する遅延も発生しないことです。The upside of this mode is that the receiver does not need to take further action on the message and is also not slowed by waiting for the outcome of the settlement. 各メッセージに含まれるデータの価値が低い、またはデータが意味を持つ時間が非常に短時間の場合は、このモードは妥当な選択です。If the data contained in the individual messages have low value and/or are only meaningful for a very short time, this mode is a reasonable choice.

ピーク ロックモードでは、受信クライアントが、受信したメッセージの明示的な解決を求めることを、ブローカーに指示します。The Peek-Lock mode tells the broker that the receiving client wants to settle received messages explicitly. 受信クライアントが処理できるよう、メッセージに排他的なロックがかけられ、他の競合受信クライアントからは認識できなくなります。The message is made available for the receiver to process, while held under an exclusive lock in the service so that other, competing receivers cannot see it. ロックの有効期間は、当初キューまたはサブスクリプション レベルで定義され、ロックを所有しているクライアントによる RenewLock 操作によって延長できます。The duration of the lock is initially defined at the queue or subscription level and can be extended by the client owning the lock, via the RenewLock operation.

メッセージにロックがかけられると、同じキューまたはサブスクリプションから受信するクライアントがロックを受け取り、アクティブなロックがかけられているメッセージではなく、次に使用可能なメッセージを取得できます。When a message is locked, other clients receiving from the same queue or subscription can take on locks and retrieve the next available messages not under active lock. メッセージのロックが明示的に解放された、またはロックの有効期限が切れた場合は、メッセージは取得順序の前または近くに戻され再送信されます。When the lock on a message is explicitly released or when the lock expires, the message pops back up at or near the front of the retrieval order for redelivery.

メッセージが受信クライアントによって繰り返し解放された、または定義されたロック回数 (maxDeliveryCount) を過ぎた場合は、メッセージはキューまたはサブスクリプションから自動的に削除され、関連する配信不能キューに配置されます。When the message is repeatedly released by receivers or they let the lock elapse for a defined number of times (maxDeliveryCount), the message is automatically removed from the queue or subscription and placed into the associated dead-letter queue.

受信側のクライアントは、正の確認を受け取ると、API レベルの Complete を呼び出して、受信メッセージの解決を開始します。The receiving client initiates settlement of a received message with a positive acknowledgment when it calls Complete at the API level. これにより、メッセージが正常に処理されたことがブローカーに通知され、メッセージはキューまたはサブスクリプションから削除されます。This indicates to the broker that the message has been successfully processed and the message is removed from the queue or subscription. ブローカーは、受信側の解決要求に対し、解決が実行できるかを示す返信を返します。The broker replies to the receiver's settlement intent with a reply that indicates whether the settlement could be performed.

受信クライアントがメッセージの処理に失敗したが、メッセージの再送を求める場合、Abandon を呼び出してメッセージの解放とロック解除を明示的に要求するか、あるいはロックの有効期限が切れるまで待つことができます。When the receiving client fails to process a message but wants the message to be redelivered, it can explicitly ask for the message to be released and unlocked instantly by calling Abandon or it can do nothing and let the lock elapse.

受信クライアントがメッセージの処理に失敗して、メッセージの再送や操作を再試行しても無駄だと判断した場合は、メッセージを拒否し、DeadLetter を呼び出すことによってメッセージを配信不能キューに移動したり、理由コードを含むカスタム プロパティを設定して、配信不能キューからメッセージを取得することもできます。If a receiving client fails to process a message and knows that redelivering the message and retrying the operation will not help, it can reject the message, which moves it into the dead-letter queue by calling DeadLetter, which also allows setting a custom property including a reason code that can be retrieved with the message from the dead-letter queue.

解決の特殊なケースは、別の記事で説明されている遅延です。A special case of settlement is deferral, which is discussed in a separate article.

Complete または Deadletter 操作だけでなく RenewLock 操作は、ネットワークの問題や、保持されているロックの有効期限が切れる、または解決を妨げる他のサーバー側の条件が存在すると、失敗することがあります。The Complete or Deadletter operations as well as the RenewLock operations may fail due to network issues, if the held lock has expired, or there are other service-side conditions that prevent settlement. 後者の場合、サービスは、API クライアントからは例外と認識される否定応答を送信します。In one of the latter cases, the service sends a negative acknowledgment that surfaces as an exception in the API clients. 理由がネットワーク接続の切断の場合は、Service Bus は、再接続による接続の AMQP リンクの回復をサポートしていないため、ロックは削除されます。If the reason is a broken network connection, the lock is dropped since Service Bus does not support recovery of existing AMQP links on a different connection.

Complete の失敗は、通常は、メッセージ最終の処理で、または処理作業から数分経ってから発生し、その場合、受信アプリケーションは作業の状態を保存するか、同じメッセージが 2 回送信されたときに無視するか、あるいは作業結果を破棄してメッセージが再送された場合と同様に再試行するかを決定できます。If Complete fails, which occurs typically at the very end of message handling and in some cases after minutes of processing work, the receiving application can decide whether it preserves the state of the work and ignores the same message when it is delivered a second time, or whether it tosses out the work result and retries as the message is redelivered.

重複するメッセージの配信を識別するための一般的な方法は、送信側によって一意の値に設定され、元の処理からの ID と紐付けられている場合もある、メッセージ ID を確認することです。The typical mechanism for identifying duplicate message deliveries is by checking the message-id, which can and should be set by the sender to a unique value, possibly aligned with an identifier from the originating process. ジョブ スケジューラは、特定のワーカーに割り当てようとしているジョブ ID にメッセージ ID を設定することが多く、ワーカーは、ジョブがすでに完了している場合は、2 回目のジョブの割り当てを無視します。A job scheduler would likely set the message-id to the identifier of the job it is trying to assign to a worker with the given worker, and the worker would ignore the second occurrence of the job assignment if that job is already done.

次の手順Next steps

Service Bus メッセージングの詳細については、次のトピックをご覧ください。To learn more about Service Bus messaging, see the following topics: