web-dev-qa-db-ja.com

失敗したAzureEvent HubeventDataをIEventProcessor.ProcessEventsメソッドに再配信するようにEventProcessorHostを強制する

アプリケーションは、.NET 4.6.1と Microsoft.Azure.ServiceBus.EventProcessorHost nugetパッケージv2.0.2 を、その依存関係 WindowsAzure.ServiceBusパッケージv3.0.1 とともに使用して処理します。 Azure EventHubメッセージ。

アプリケーションにはIEventProcessorの実装があります。未処理の例外がProcessEventsAsyncメソッドからスローされた場合、EventProcessorHostはそれらのメッセージを実行中のIEventProcessorのインスタンスに再送信しません。 (逸話的に、ホスティングアプリケーションが停止して再起動した場合、またはリースが失われて再取得された場合は、再送信されます。)

例外が発生したイベントメッセージをEventProcessorHostによってIEventProcessor実装に強制的に再送信する方法はありますか?

考えられる解決策の1つは、ほぼ同じ質問に対するこのコメントで示されています。 IEventProcessor.ProcessEventsAsyncで未処理のEventHubメッセージを再配信する

コメントは、最後に正常に処理されたイベントメッセージのコピーを保持し、ProcessEventsAsyncで例外が発生したときにそのメッセージを使用して明示的にチェックポイントを設定することを提案しています。ただし、そのようなソリューションを実装してテストした後でも、EventProcessorHostは再送信されません。実装は非常に簡単です。

private EventData _lastSuccessfulEvent;

public async Task ProcessEventsAsync(
    PartitionContext context,
    IEnumerable<EventData> messages)
{
    try
    {
        await ProcessEvents(context, messages);     // does actual processing, may throw exception
        _lastSuccessfulEvent = messages
            .OrderByDescending(ed => ed.SequenceNumber)
            .First();
    }
    catch(Exception ex)
    {
        await context.CheckpointAsync(_lastSuccessfulEvent);
    }
}

実行中の事柄の分析: enter image description here

部分的なログサンプルはこちらから入手できます: https://Gist.github.com/ttbjj/4781aa992941e00e4e15e0bf1c45f316#file-gistfile1-txt

37
Chrisgh

[〜#〜] tldr [〜#〜]:失敗したバッチを再生する唯一の信頼できる方法 _IEventProcessor.ProcessEventsAsync_へのイベントの数は-ShutdownEventProcessorHost(別名EPH)すぐに-eph.UnregisterEventProcessorAsync()または プロセスの終了 -状況に応じて。これにより、他のEPHインスタンスがこのパーティションのリースを取得し、前のチェックポイントから開始できるようになります。

これを説明する前に-私はそれを強調したいのですが、これは素晴らしい質問であり、実際、私たちがしなければならなかった最も難しい設計上の選択の1つでしたEPH。私の見解では、それはusabilityフレームワークのsupportability/EPHと_Technical-Correctness_のトレードオフでした。

理想的な状況は次のようになります:_IEventProcessorImpl.ProcessEventsAsync_のユーザーコードが例外をスローした場合-EPHライブラリはキャッチすべきではありませんこの。このException-プロセスをクラッシュさせ、_crash-dump_はcallstackの責任を明確に示しているはずです。私はまだ信じています-これは最も_technically-correct_の解決策です。

現在の状況:_IEventProcessorImpl.ProcessEventsAsync_ APIとEPHの契約は、

  1. EventDataEventHubsサービスから受信できる限り-_IEventProcessorImplementation.ProcessEventsAsync_でユーザーコールバック(_EventData's_)を呼び出し続けます。ユーザーコールバックは、呼び出し中にエラーをスローします。_EventProcessorOptions.ExceptionReceived_に通知してください。
  2. _IEventProcessorImpl.ProcessEventsAsync_内のユーザーコードはすべてのエラーを処理し、必要に応じて_Retry's_を組み込む必要があります。 EPHは、ユーザーが処理時間を完全に制御できるようにするために、このコールバックにタイムアウトを設定しません。
  3. 特定のイベントが問題の原因である場合-EventDataに特別なプロパティをマークします-ex:type = _poison-event_の場合、同じEventHubに再送信します(ポインタを含めます)実際のイベントに、これらの_EventData.Offset_とSequenceNumberを新しい_EventData.ApplicationProperties_)にコピーするか、SERVICEBUSキューに転送するか、基本的にpoison-eventの処理を特定して延期します。
  4. 考えられるすべてのケースを処理しても、まだExceptionsに遭遇している場合は、この例外を除いて、プロセスをキャッチしてEPHまたはfailfastをシャットダウンします。 EPHが戻ってくると、そこから始まります。

「古いイベント」のチェックポイントが機能しないのはなぜですかEPHを一般的に理解するには this を読んでください)::

舞台裏では、EPHはEventHubConsumergroupパーティションのレシーバーごとにポンプを実行しています。そのジョブは、指定されたcheckpoint(存在する場合)からレシーバーを起動し、IEventProcessor実装し、チェックポイント内の指定されたreceiveから指定されたEventHubパーティションからOffset(存在しない場合-_EventProcessorOptions.initialOffsetProvider_)、最終的に_IEventProcessorImpl.ProcessEventsAsync_を呼び出します。 Checkpointの目的は、EPHプロセスがシャットダウンし、パーティションの所有権が別のEPHインスタンスに移動したときに、メッセージの処理を確実に開始できるようにすることです。したがって、checkpoint[〜#〜] pump [〜#〜]の開始時にのみ消費され、[ポンプが始動すると、〜#〜] not [〜#〜]が読み取られます。

私がこれを書いているとき、EPHはバージョン 2.2.1 です。

イベントハブに関するより一般的な読み物...

14