Partager via


Envoyer des demandes parallèles

Lorsque votre application doit envoyer un grand nombre de requêtes à Dataverse, vous pouvez obtenir un débit total beaucoup plus élevé en envoyant des requêtes en parallèle à l’aide de plusieurs threads. Dataverse est conçu pour prendre en charge plusieurs utilisateurs simultanés. L’envoi de demandes en parallèle tire ainsi parti de cette force.

Note

L’envoi de requêtes parallèles dans un plug-in n’est pas pris en charge. Pour plus d’informations, consultez Ne pas utiliser l’exécution parallèle dans les plug-ins et les activités de flux de travail.

Degré de parallélisme (DOP) optimal

Dataverse gère l’allocation de ressources pour les environnements. Les environnements de production que de nombreux utilisateurs sous licence utilisent fortement ont davantage de ressources allouées. Le nombre et les fonctionnalités des serveurs alloués peuvent varier au fil du temps. Il n’existe donc aucun nombre fixe pour le degré optimal de parallélisme. Utilisez plutôt la valeur entière renvoyée par l’en-tête de réponse x-ms-dop-hint. Cette valeur fournit un degré de parallélisme recommandé pour l’environnement.

Lorsque vous utilisez la programmation parallèle dans .NET, le degré de parallélisme par défaut dépend du nombre de cœurs d’UC sur le client exécutant le code. Si le nombre de cœurs de processeur dépasse la meilleure correspondance pour l’environnement, vous envoyez peut-être trop de demandes. Définissez la propriété ParallelOptions.MaxDegreeOfParallelism pour définir un nombre maximal de tâches simultanées.

Limites de la protection des services

L’une des trois facettes surveillées dans le cadre des limites de protection des services est le nombre de requêtes simultanées. Par défaut, cette valeur est 52, mais elle peut être supérieure. Une erreur est retournée si la limite est dépassée. Si vous dépendez de la x-ms-dop-hint valeur d’en-tête de réponse pour limiter le degré de parallélisme, vous devez rarement atteindre cette limite. Si vous rencontrez cette erreur, réduisez le nombre de threads simultanés.

Une erreur spécifique est retournée lorsque cette limite est atteinte :

Code d’erreur Code hexadécimal Message
-2147015898 0x80072326 Number of concurrent requests exceeded the limit of 52.

Vous pouvez également réduire la probabilité de cette erreur en envoyant vos demandes à tous les serveurs qui prennent en charge l’environnement en désactivant l’affinité du serveur.

Affinité de serveur

Lorsque vous vous connectez à un service sur Azure, le service retourne un cookie avec la réponse. Toutes vos demandes suivantes tentent d’accéder au même serveur, sauf si la gestion de la capacité force la demande à accéder à un autre serveur. Les applications clientes interactives, en particulier les clients de navigateur, bénéficient de ce cookie, car elle permet à l’application de réutiliser les données mises en cache sur le serveur. Les navigateurs web ont toujours activé l’affinité du serveur et vous ne pouvez pas le désactiver.

Lorsque vous envoyez des demandes en parallèle à partir de votre application cliente, vous pouvez bénéficier d’avantages en matière de performances en désactivant ce cookie. Chaque requête que vous envoyez est acheminée vers l'un des serveurs éligibles. Cette modification augmente non seulement le débit total, mais elle permet également de réduire l’impact des limites de protection du service, car chaque limite s’applique par serveur.

Les exemples suivants montrent comment désactiver l’affinité du serveur à l’aide de .NET.

Si vous utilisez les classes ServiceClient ou CrmServiceClient , ajoutez le code suivant au AppSettings nœud du App.config fichier.

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

Vous pouvez également définir la valeur de la EnableAffinityCookie propriété à l’aide des classes ServiceClient ou CrmServiceClient .

Vous pouvez également définir cette propriété à l’aide du constructeur ServiceClient(ConnectionOptions, Boolean, ConfigurationOptions) et de la propriété ConfigurationOptions.EnableAffinityCookie .

Optimiser votre connexion

Lorsque vous utilisez .NET et envoyez des demandes en parallèle, modifiez les paramètres par défaut afin que vos demandes ne soient pas limitées par eux. Effectuez les modifications suivantes :

// 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

Ce paramètre contrôle le nombre minimal de threads que le pool de threads crée à la demande à mesure que de nouvelles requêtes sont entrantes. Après avoir atteint ce nombre, le pool de threads bascule vers un algorithme qui gère la création et la destruction des threads.

Par défaut, le nombre minimum de threads est défini sur le nombre de processeurs. Utilisez SetMinThreads pour augmenter le nombre minimal de threads. Par exemple, vous pouvez augmenter temporairement le nombre pour contourner les problèmes où certains éléments de tâche ou tâches mis en file d’attente bloquent les threads du pool. Ces blocages conduisent parfois à une situation où tous les threads de travail ou d’achèvement d’E/S sont bloqués (privation). Cependant, l’augmentation du nombre minimum de threads peut dégrader les performances d’autres manières.

Les nombres que vous utilisez peuvent varier en fonction du matériel. Les nombres que vous utilisez sont inférieurs à ceux utilisés pour une fonction Azure basée sur la consommation que pour le code s’exécutant sur un hôte dédié avec du matériel haut de gamme.

Pour plus d’informations, voir System.Threading.ThreadPool.SetMinThreads

Paramètres System.Net.ServicePointManager

Dans .NET Framework, ServicePointManager est une classe statique que vous utilisez pour créer, gérer et supprimer des instances de la classe ServicePoint . Utilisez ces paramètres avec les classes ServiceClient ou CrmServiceClient. Ces paramètres doivent également s’appliquer lors de l’utilisation de HttpClient avec l’API web dans .NET Framework. Toutefois, avec .NET Core, Microsoft recommande de configurer dans HttpClient à la place.

DefaultConnectionLimit

Le matériel limite finalement cette valeur. Si vous le définissez trop élevé, d’autres mécanismes le limitent. Augmentez-la au-dessus de la valeur par défaut et définissez-la au moins égale au nombre de requêtes simultanées que vous prévoyez d'envoyer.

Lorsque vous utilisez .NET Core avec HttpClient, la propriété HttpClientHandler.MaxConnectionsPerServer contrôle ce paramètre. Sa valeur par défaut est int. MaxValue.

Pour en savoir plus, consultez :

Expect100Continue

Lorsque vous définissez cette propriété sur true, le client attend une confirmation aller-retour d’une connexion au serveur. Pour HttpClient, la valeur par défaut de HttpRequestHeaders.ExpectContinue est false.

Pour en savoir plus, consultez :

UseNagleAlgorithm

L’algorithme Nagle réduit le trafic réseau en mettant en mémoire tampon de petits paquets de données et en les transmettant sous forme de paquet unique. Ce processus est également appelé « nagling ». Il est largement utilisé, car il réduit le nombre de paquets transmis et réduit la surcharge par paquet. La définition de cette valeur sur false peut réduire la surcharge globale de transmission, mais peut entraîner un retard dans l’arrivée du paquet de données.

Pour plus d’informations, consultez System.Net.ServicePointManager.UseNagleAlgorithm.

Exemples

Les exemples .NET suivants montrent comment utiliser la bibliothèque parallèle de tâches (TPL) avec Dataverse.

Vous pouvez accéder à la valeur de réponse x-ms-dop-hint par le biais de la propriété RecommendedDegreesOfParallelism dans l'une ou l'autre des options ServiceClient ou CrmServiceClient. Utilisez cette valeur lors de la définition de ParallelOptions.MaxDegreeOfParallelism lorsque vous utilisez Parallel.ForEach.

Ces exemples montrent également la définition de la propriété EnableAffinityCookie sur false.

Dans les exemples suivants, ajoutez les valeurs d’ID des réponses à un ConcurrentBag de GUID. ConcurrentBag fournit une collection d’objets non ordonnée sécurisée pour les threads lorsque l’ordre n’a pas d’importance. Vous ne pouvez pas vous attendre à ce que l’ordre des GUID retournés par cette méthode corresponde à l’ordre des éléments envoyés dans le entityList paramètre.

Utilisation de ServiceClient avec .NET 6 ou version ultérieure

En utilisant .NET 6 ou version ultérieure, vous pouvez utiliser la méthode Parallel.ForEachAsync avec les méthodes asynchrones incluses avec ServiceClient, telles que CreateAsync.

/// <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();
}

Utilisation de CrmServiceClient avec .NET Framework

Lorsque vous utilisez .NET Framework, la méthode Clone disponible dans CrmServiceClient duplique une connexion existante à Dataverse afin de pouvoir utiliser la méthode Parallel.ForEach .

/// <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();
}

Voir aussi

Limites de l’API de protection des services
Exemple d′opérations parallèles de l′API web WebApiService (C#)
Exemple d’opérations parallèles d’API web avec des composants de flux de données TPL (C#)
Exemple : Utiliser la bibliothèque parallèle de tâches avec CrmServiceClient