次の方法で共有


並行要求を送信する

アプリケーションが大量の要求を Dataverse に送信する必要がある場合は、複数のスレッドを使用して要求を並列で送信することで、はるかに高い合計スループットを実現できます。 Dataverse は複数の同時ユーザーをサポートするように設計されているため、リクエストを並行して送信すると、この強みが活かされます。

注意

プラグイン内での並列要求の送信はサポートされていません。 詳細については、「 プラグインおよびワークフロー アクティビティ内で並列実行を使用しない」を参照してください。

並列処理の最適度 (DOP)

Dataverse は、環境のリソース割り当てを管理します。 多くのライセンスユーザーが頻繁に使用する運用環境には、割り当てられたリソースが多くなります。 割り当てられるサーバーの数と機能は時間の経過と同時に変化する可能性があるため、並列処理の最適な次数は決まっていません。 代わりに、x-ms-dop-hint 応答ヘッダーから返された整数値を使用してください。 この値は、環境に推奨される並列度を示します。

.NET で並列プログラミングを使用する場合、並列処理の既定の次数は、コードを実行しているクライアント上の CPU コアの数によって異なります。 CPU コアの数が環境に最適な数を超えている場合は、送信する要求が多すぎる可能性があります。 ParallelOptions.MaxDegreeOfParallelism プロパティを設定して、同時実行タスクの最大数を定義します。

サービス保護の制限

サービス保護の制限について監視される 3 つのファセットの 1 つは、同時要求の数です。 既定では、この値は 52 ですが、より高い場合があります。 制限を超えると、エラーが返されます。 並列処理の次数を制限するために x-ms-dop-hint 応答ヘッダー値に依存している場合、この制限に達することはめったにありません。 このエラーが発生した場合は、同時実行スレッドの数を減らします。

この制限に達すると、特定のエラーが返されます。

エラー コード 16進コード メッセージ
-2147015898 0x80072326 Number of concurrent requests exceeded the limit of 52.

また、サーバー アフィニティを無効にして、環境をサポートするすべてのサーバーに要求を送信することで、このエラーの可能性を減らすこともできます。

サーバー アフィニティ

Azure 上のサービスに接続すると、サービスは応答を含む Cookie を返します。 容量管理によって要求が強制的に別のサーバーに送信されない限り、後続のすべての要求は同じサーバーに移動しようとします。 対話型クライアント アプリケーション (特にブラウザー クライアント) は、サーバーにキャッシュされたデータをアプリケーションで再利用できるため、この Cookie を利用できます。 Web ブラウザーでは常にサーバー アフィニティが有効になっており、無効にすることはできません。

クライアント アプリケーションから要求を並列で送信する場合は、この Cookie を無効にすることでパフォーマンス上の利点を得ることができます。 送信する各要求は、いずれかの対象サーバーにルーティングされます。 この変更により、合計スループットが増加するだけでなく、各制限がサーバーごとに適用されるため、サービス保護の制限の影響を軽減するのにも役立ちます。

次の例は、.NET を使用してサーバー アフィニティを無効にする方法を示しています。

ServiceClient クラスまたは CrmServiceClient クラスを使用している場合は、AppSettings ファイルの App.config ノードに次のコードを追加します。

<add key="PreferConnectionAffinity" value="false" />

EnableAffinityCookie クラスまたは CrmServiceClient クラスを使用して、 プロパティの値を設定することもできます。

このプロパティは、 ServiceClient(ConnectionOptions、Boolean、ConfigurationOptions) コンストラクターと ConfigurationOptions.EnableAffinityCookie プロパティを使用して設定することもできます。

接続の最適化

.NET を使用して要求を並列で送信する場合は、要求が制限されないように既定の設定を変更します。 次の変更を行います。

// Bump up the min threads reserved for this app to ramp connections faster - minWorkerThreads defaults to 4, minIOCP defaults to 4 
ThreadPool.SetMinThreads(100, 100);
// Change max connections from .NET to a remote service default: 2
System.Net.ServicePointManager.DefaultConnectionLimit = 65000;
// Turn off the Expect 100 to continue message - 'true' will cause the caller to wait until it round-trip confirms a connection to the server 
System.Net.ServicePointManager.Expect100Continue = false;
// Can decrease overall transmission overhead but can cause delay in data packet arrival
System.Net.ServicePointManager.UseNagleAlgorithm = false;

ThreadPool.SetMinThreads

この設定は、新しい要求が入ってくるとスレッド プールがオンデマンドで作成するスレッドの最小数を制御します。 この数に達すると、スレッド プールは、スレッドの作成と破棄を管理するアルゴリズムに切り替わります。

デフォルトでは、スレッドの最小数はプロセッサ数に設定されています。 SetMinThreadsを使用して、スレッドの最小数を増やします。 たとえば、キューに置かれた作業項目やタスクによってスレッド プール スレッドがブロックされる問題を回避するために、一時的に数を増やすことができます。 これらのブロックにより、すべてのワーカーまたはI/O完了スレッドがブロックされる (枯渇) 状況が発生することがあります。 ただし、スレッドの最小数を増やすと、他の方法でパフォーマンスが低下する可能性があります。

使用する数値は、ハードウェアによって異なる場合があります。 従量課金ベースの Azure 関数に使用する数値は、ハイエンド ハードウェアを搭載した専用ホストで実行されるコードよりも低くなります。

詳細: System.Threading.ThreadPool.SetMinThreads

System.Net.ServicePointManager の設定

.NET Framework では、 ServicePointManager は、 ServicePoint クラスのインスタンスの作成、保守、削除に使用する静的クラスです。 これらの設定は、ServiceClient クラス、または CrmServiceClient クラスで使用します。 これらの設定は、.NET Framework で Web API で HttpClient を 使用する場合にも適用する必要があります。 ただし、.NET Core では、代わりに HttpClient の設定をお勧めします。

DefaultConnectionLimit

最終的には、ハードウェアによってこの値が制限されます。 設定が高すぎると、他のメカニズムによって制限されます。 既定値より上にして、送信する同時要求の数と少なくとも同じ数に設定します。

HttpClientで .NET Core を使用すると、HttpClientHandler.MaxConnectionsPerServer プロパティによってこの設定が制御されます。 既定値は int です。MaxValue

詳細については、以下を参照してください:

Expect100Continue

このプロパティを true に設定すると、クライアントはサーバーへの接続のラウンドトリップ確認を待機します。 HttpClientの場合、HttpRequestHeaders.ExpectContinue の既定値は false です。

詳細については、以下を参照してください:

UseNagleAlgorithm

Nagle アルゴリズムは、データの小さなパケットをバッファリングし、それらを 1 つのパケットとして送信することで、ネットワーク トラフィックを削減します。 このプロセスは"ナグリング" とも呼ばれます。送信されるパケットの数が減り、パケットあたりのオーバーヘッドが減少するため、広く使用されています。 この値を false に設定すると、全体的な送信オーバーヘッドが減少する可能性がありますが、データ パケット到着の遅延が発生する可能性があります。

詳細については、System.Net.ServicePointManager.UseNagleAlgorithmを参照してください。

使用例

次の .NET の例は、 Dataverse でタスク並列ライブラリ (TPL) を使用する方法を示しています。

x-ms-dop-hintまたはServiceClient プロパティを使用して、CrmServiceClient応答値にアクセスできます。 Parallel.ForEach を使用するときに ParallelOptions.MaxDegreeOfParallelism を設定するときに、この値を使用します。

これらの例では、EnableAffinityCookie プロパティを false に設定することも示しています。

次の例では、応答の ID 値を GUID の ConcurrentBag に追加します。 ConcurrentBag 順序が問題にならない場合に、オブジェクトのスレッドセーフな順序付けされていないコレクションを提供します。 このメソッドによって返される GUID の順序が、 entityList パラメーターで送信された項目の順序と一致するとは思えません。

.NET 6 以降で ServiceClient を使用する

.NET 6 以降を使用すると、CreateAsync などの ServiceClient に含まれる非同期メソッドで Parallel.ForEachAsync メソッドを使用できます。

/// <summary>
/// Creates records in parallel
/// </summary>
/// <param name="serviceClient">The authenticated ServiceClient instance.</param>
/// <param name="entityList">The list of entities to create.</param>
/// <returns>The id values of the created records.</returns>
static async Task<Guid[]> CreateRecordsInParallel(
    ServiceClient serviceClient, 
    List<Entity> entityList)
{
    ConcurrentBag<Guid> ids = new();

    // Disable affinity cookie
    serviceClient.EnableAffinityCookie = false;

    var parallelOptions = new ParallelOptions()
    { MaxDegreeOfParallelism = 
        serviceClient.RecommendedDegreesOfParallelism };

    await Parallel.ForEachAsync(
        source: entityList,
        parallelOptions: parallelOptions,
        async (entity, token) =>
        {
            ids.Add(await serviceClient.CreateAsync(entity, token));
        });

    return ids.ToArray();
}

.NET Framework での CrmServiceClient の使用

.NET Framework を使用する場合、CrmServiceClient で使用できる Clone メソッドは、Parallel.ForEach メソッドを使用できるように、Dataverse への既存の接続を複製します。

/// <summary>
/// Creates records in parallel
/// </summary>
/// <param name="crmServiceClient">The authenticated CrmServiceClient instance.</param>
/// <param name="entityList">The list of entities to create.</param>
/// <returns>The id values of the created records.</returns>
static Guid[] CreateRecordsInParallel(
    CrmServiceClient crmServiceClient, 
    List<Entity> entityList)
{
   ConcurrentBag<Guid> ids = new ConcurrentBag<Guid>();

    // Disable affinity cookie
    crmServiceClient.EnableAffinityCookie = false;

   Parallel.ForEach(entityList,
      new ParallelOptions()
      {
            MaxDegreeOfParallelism = crmServiceClient.RecommendedDegreesOfParallelism
      },
      () =>
      {
            //Clone the CrmServiceClient for each thread
            return crmServiceClient.Clone();
      },
      (entity, loopState, index, threadLocalSvc) =>
      {
            ids.Add(threadLocalSvc.Create(entity));

            return threadLocalSvc;
      },
      (threadLocalSvc) =>
      {
            //Dispose the cloned crmServiceClient instance
            threadLocalSvc?.Dispose();
      }
   );
   return ids.ToArray();
}

参照

サービス保護の API 制限
Web API WebApiService の並列演算のサンプル (C#)
TPL データフロー コンポーネントを使用した Web API 並列演算のサンプル (C#)
サンプル: CrmServiceClient を使用したタスク並列ライブラリ