web-dev-qa-db-ja.com

メッセージを再試行する前にAzureService Busを遅延させることはできますか?

Azure Service Busは、組み込みの再試行メカニズムをサポートしており、放棄されたメッセージを別の読み取り試行ですぐに表示できます。このメカニズムを使用して一時的なエラーを処理しようとしていますが、メッセージは破棄された直後に利用可能になります。

私がやりたいのは、メッセージが放棄された後、できれば指数関数的に増加するポリシーに基づいて、メッセージを一定期間非表示にすることです。

メッセージを破棄するときにScheduledEnqueueTimeUtcプロパティを設定しようとしましたが、効果がないようです。

var messagingFactory = MessagingFactory.CreateFromConnectionString(...);

var receiver = messagingFactory.CreateMessageReceiver("test-queue");

receiver.OnMessageAsync(async brokeredMessage =>
{
    await brokeredMessage.AbandonAsync(
        new Dictionary<string, object>
        {
            { "ScheduledEnqueueTimeUtc", DateTime.UtcNow.AddSeconds(30) }
        });
    }
});

メッセージをまったく放棄せず、ロックを期限切れにすることを検討しましたが、これには、MessageReceiverがメッセージのロック期間を指定する方法に影響を与える方法が必要であり、何も見つかりません。この値を変更できるようにするAPI。さらに、ロックがすでに必要になるまで、メッセージの配信数を読み取ることはできません(したがって、次の再試行を待機する時間の決定を行うことはできません)。

メッセージバスの再試行ポリシーに何らかの影響を与えることはできますか、それとも他の方法で人為的に遅延を導入することはできますか?

23
Paul Turner

私は実際に 昨年同じ質問をしました (実装は別として)APIを見て考えることができる3つのアプローチを使用しました。 SBチームで働く@ClemensVastersは、Deferを何らかの再受信とともに使用することが、これを正確に制御する唯一の方法であると回答しました。

セカンダリキューを使用して、どのプライマリメッセージが延期され、メインキューから再受信する必要があるかを示すメッセージを格納することをお勧めする、具体的なアプローチについての彼の回答に対する私のコメントを読むことができます。次に、これらのセカンダリメッセージのScheduledEnqueueTimeUtcを設定して、再試行するまでの待機時間を正確に正確に制御することで、待機時間を制御できます。

11
Drew Marsh

ここで注意してください。再試行機能を、Completeイベント駆動型メッセージ処理の自動Abandon/OnMessageメカニズムと混同していると思います。組み込みの再試行メカニズムは、ServiceBusへの呼び出しが失敗したときに機能します。たとえば、メッセージを完了として設定するために呼び出して失敗した場合、再試行メカニズムが作動します。メッセージを処理している場合、再試行機能による再試行をトリガーしない例外が独自のコードで発生します。エラーがコードによるものなのか、サービスバスに接続しようとしたときなのか、質問は明確になりません。

サービスバスとの通信を試みてエラーが発生したときに発生する再試行ポリシーを実際に変更した後は、RetryPolicy自体に設定されているMessageReciverを変更できます。デフォルトで使用されるRetryExponitialと、独自に作成できる抽象RetryPolicyがあります。

あなたが求めているのは、処理中に例外が発生したときに何が起こるかをより細かく制御できることであり、そのメッセージの処理を延期したいと考えています。いくつかのオプションがあります:

メッセージハンドラを作成するときに、 OnMessageOptions を設定できます。プロパティの1つは「オートコンプリート」です。デフォルトでは、これはtrueに設定されています。つまり、メッセージの処理が完了するとすぐに、Completeメソッドが自動的に呼び出されます。例外が発生すると、放棄が自動的に呼び出されます。これが表示されています。オートコンプリートをfalseに設定することにより、メッセージハンドラー内から自分でコンプリートを呼び出す必要がありました。そうしないと、メッセージロックが最終的に不足します。これは、探している動作の1つです。

したがって、処理中に例外が発生した場合にCompleteを呼び出さないようにハンドラーを作成できます。その後、メッセージはロックがなくなるまでキューに残り、その後再び使用可能になります。標準のデッドレタリングメカニズムが適用され、x回の試行の後、自動的にデッドレターキューに入れられます。

この方法で処理する場合の注意点は、あらゆるタイプの例外がこのように処理されることです。どのタイプの例外がこれを行っているのか、そして本当に処理をプッシュオフしたいかどうかを本当に考える必要があります。たとえば、処理中にサードパーティのシステムを呼び出していて、それが例外を与える場合、それは一時的なものであり、すばらしいものです。ただし、大きな問題になることがわかっているエラーが発生した場合は、メッセージに頼るだけでなく、システムで他のことを行うことを決定する場合があります。

Defer」メソッドも確認できます。このメソッドは、実際には、シーケンス番号によって特にプルされない限り、そのメッセージをキューから処理することを許可しません。コードはシーケンス番号の値を覚えてプルする必要があります。しかし、これはあなたが説明したこととはまったく異なります。

もう1つのオプションは、メッセージを処理するイベント駆動型のOnMessageから離れることです。これは非常に役立ちますが、物事をあまり制御することはできません。代わりに、独自の処理ループを接続し、放棄/完了を自分で処理します。また、OnMessageパターンが提供するスレッド化/並行呼び出し管理の一部を処理する必要があります。これはより多くの作業になる可能性がありますが、究極の柔軟性があります。

最後に、変更したいプロパティを渡すAbandonAsyncへの呼び出しが機能しなかった理由は、それらのプロパティが標準ではなくメソッドで メタデータプロパティ を参照しているためだと思いますBrokeredMessageのプロパティ。

14
MikeWo

注文ピッキングシステムがレガシーであり、毎晩メンテナンスモードに入るという同様の問題に遭遇しました。

この記事のアイデアを使用して( https://markheath.net/post/defer-processing-Azure-service-bus-message )メッセージが何回あったかを追跡するカスタムプロパティを作成しました10回試行した後、メッセージを再送信し、手動でデッドレタリングしました。メッセージの再試行回数が10回未満の場合、メッセージのクローンが作成され、カスタムプロパティがインクリメントされ、新しいメッセージのエンキューが設定されます。

using Microsoft.Azure.ServiceBus;
public PickQueue()
{
    queueClient = new QueueClient(QUEUE_CONN_STRING, QUEUE_NAME); 
}
public async Task QueueMessageAsync(int OrderId)
{
    string body = JsonConvert.SerializeObject(OrderId);
    var message = new Message(Encoding.UTF8.GetBytes(body));
    await queueClient.SendAsync(message);
}

public async Task ReQueueMessageAsync(Message message, DateTime utcEnqueueTime)
{
    int resubmitCount = (int)(message.UserProperties["ResubmitCount"] ?? 0) + 1;
    if (resubmitCount > 10)
    {
        await queueClient.DeadLetterAsync(message.SystemProperties.LockToken);
    }
    else
    {
        Message clone = message.Clone();
        clone.UserProperties["ResubmitCount"] = ++resubmitCount;
        await queueClient.ScheduleMessageAsync(message, utcEnqueueTime);
    }
}
1
Tony