Partager via


Envoyer des demandes parallèles

Lorsque votre application doit envoyer un grand nombre de demandes à Dataverse vous pouvez obtenir un débit total beaucoup plus élevé en envoyant des demandes 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.

Notes

L’envoi de demandes parallèles dans un plug-in n’est pas pris en charge. Pour plus d’informations : Ne pas utiliser l’exécution parallèle dans les plug-ins et les activités de workflow

Degré de parallélisme (DOP) optimal

La gestion de l’allocation des ressources pour les environnements fait partie du service fourni par Dataverse. Les environnements de production qui sont fortement utilisés par de nombreux utilisateurs sous licence se verront attribuer davantage de ressources. Le nombre de serveurs alloués et les capacités de ces serveurs peuvent varier au fil du temps, il n’y a donc pas de nombre fixe que vous devez appliquer pour obtenir le degré de parallélisme optimal. 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.

Lors de l’utilisation de la Programmation parallèle dans .NET, le degré de parallélisme par défaut dépend du nombre de cœurs de processeur sur le client exécutant le code. Si le nombre de cœurs de processeur dépasse la meilleure correspondance pour l’environnement, cela peut signifier que vous envoyez trop de demandes. Vous pouvez définir la propriété ParallelOptions.MaxDegreeOfParallelism pour définir un nombre maximum 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 sera renvoyée si la limite est dépassée. Si vous dépendez de la valeur de l’en-tête de réponse x-ms-dop-hint pour limiter le degré de parallélisme, vous devez rarement atteindre cette limite. Si vous rencontrez cette erreur, vous devez réduire le nombre de threads simultanés.

Une erreur spécifique est renvoyé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 atténuer la probabilité que cette erreur se produise en envoyant vos demandes à tous les serveurs qui prennent en charge l’environnement, en désactivant l’affinité de serveur.

Affinité de serveur

Lorsque vous établissez une connexion à un service sur Azure, un cookie est renvoyé avec la réponse et toutes vos demandes suivantes tenteront d’être acheminées vers le même serveur à moins que la gestion de la capacité ne l’oblige à aller sur un autre serveur. Les applications clientes interactives, en particulier les clients du navigateur, en tirent parti, car cela permet de réutiliser les données mises en cache sur le serveur. L’affinité de serveur activée est toujours activée sur les navigateurs web et elle ne peut pas être désactivée.

Lors de l’envoi de requêtes en parallèle depuis votre application cliente, vous pouvez améliorer les performances en désactivant ce cookie. Chaque demande que vous envoyez sera acheminée vers l’un des serveurs éligibles. Non seulement cela augmente le débit total, mais cela aide également à réduire l’impact des limites de protection des services, car chaque limite est appliquée par serveur.

Voici quelques exemples montrant comment désactiver l’affinité de serveur avec .NET.

Si vous utilisez les classes ServiceClient ou CrmServiceClient, ajoutez les éléments suivants au nœud AppSettings dans le fichier App.config.

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

Vous pouvez également définir la valeur de la propriété EnableAffinityCookie avec ServiceClient ou CrmServiceClient

Cela peut également le faire à l’aide du constructeur ServiceClient(ConnectionOptions, Boolean, ConfigurationOptions), en utilisant la propriété ConfigurationOptions.EnableAffinityCookie.

Optimiser votre connexion

Lorsque vous utilisez .NET et que vous envoyez des requêtes en parallèle, appliquez les modifications de configuration comme suit afin que vos requêtes ne soient pas limitées par les paramètres par défaut :

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

Définit le nombre minimum de threads que le pool de threads crée à la demande, au fur et à mesure que de nouvelles demandes sont faites, avant de passer à un algorithme de gestion de la création et de la destruction des threads.

Par défaut, le nombre minimum de threads est défini sur le nombre de processeurs. Vous pouvez utiliser SetMinThreads pour augmenter le nombre minimum de threads, par exemple pour contourner temporairement des problèmes où certains éléments de travail ou tâches en file d’attente bloquent les threads du pool de threads. 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.

La quantité que vous devez utiliser peut varier en fonction du matériel. La quantité que vous utilisez doit être inférieure pour une fonction Azure basée sur la consommation que pour le code qui s’exécute sur un hôte dédié avec du matériel haut de gamme.

Pour plus d’informations : System.Threading.ThreadPool.SetMinThreads

System.Net.ServicePointManager settings

Avec .NET Framework, ServicePointManager est une classe statique utilisée pour créer, maintenir 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, mais avec .NET Core, Microsoft recommande plutôt les paramètres de HttpClient.

DefaultConnectionLimit

Cette valeur est finalement limitée par le matériel. Si le réglage est trop élevé, elle sera limitée par d’autres moyens. Le point clé est qu’elle doit être supérieure à la valeur par défaut et au moins égal au nombre de demandes simultanées que vous avez l’intention d’envoyer.

Avec .NET Core utilisant HttpClient, ceci est contrôlé par HttpClientHandler.MaxConnectionsPerServer et la valeur par défaut est int.MaxValue.

Pour plus d′informations :

Expect100Continue

Lorsque cette propriété est définie sur true, le client patiente jusqu’à ce qu’un aller-retour confirme une connexion au serveur. Pour HttpClient la valeur par défaut de HttpRequestHeaders.ExpectContinue est false.

Pour plus d′informations :

UseNagleAlgorithm

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

Pour plus d’informations : System.Net.ServicePointManager.UseNagleAlgorithm

Examples

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

La valeur de réponse x-ms-dop-hint est disponible via la propriété RecommendedDegreesOfParallelism dans ServiceClient ou CrmServiceClient. Vous devez utiliser 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 ci-dessous, les valeurs d’ID des réponses sont ajoutées à un ConcurrentBag de Guids. ConcurrentBag fournit une collection d’objets non ordonnée thread-safe lorsque l’ordre n’a pas d’importance. L’ordre des Guids renvoyés par cette méthode peut ne pas correspondre à l’ordre des éléments envoyés dans le paramètre entityList.

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

Avec .NET 6 ou version ultérieure, vous pouvez utiliser la méthode Parallel.ForEachAsync avec les méthodes asynchrones incluses avec ServiceClient, comme 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

Lors de l’utilisation de .NET Framework, la méthode Clone disponible dans CrmServiceClient permet de dupliquer une connexion existante vers Dataverse afin que vous puissiez 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

Notes

Pouvez-vous nous indiquer vos préférences de langue pour la documentation ? Répondez à un court questionnaire. (veuillez noter que ce questionnaire est en anglais)

Le questionnaire vous prendra environ sept minutes. Aucune donnée personnelle n’est collectée (déclaration de confidentialité).