Compartir vía


Enviar solicitudes paralelas

Cuando su aplicación necesita enviar una gran cantidad de solicitudes a Dataverse, puede lograr un rendimiento total mucho mayor si envía solicitudes en paralelo mediante varios subprocesos. Dataverse está diseñado para admitir múltiples usuarios simultáneos, por lo que el envío de solicitudes en paralelo aprovecha esta fortaleza.

Nota

No se admite el envío de solicitudes paralelas dentro de un complemento. Más información: No utilizar ejecución en paralelo en complementos y actividades de flujo de trabajo

Grado de paralelismo óptimo (DOP)

Parte del servicio que Dataverse proporciona es administrar la asignación de recursos para entornos. Los entornos de producción que son muy utilizados por muchos usuarios con licencia tendrán más recursos asignados. El número y las capacidades de los servidores asignados pueden variar con el tiempo, por lo que no hay un número fijo que deba aplicar para obtener el grado óptimo de paralelismo. En su lugar, utilice el valor entero devuelto por el encabezado de respuesta x-ms-dop-hint. Este valor proporciona un grado recomendado de paralelismo para el entorno.

Cuando usa Programación en paralelo en .NET, el grado predeterminado de paralelismo depende del número de núcleos de CPU en el cliente que ejecuta el código. Si el número de núcleos de CPU supera la mejor coincidencia para el entorno, es posible que esté enviando demasiadas solicitudes. Puede configurar la propiedad ParallelOptions.MaxDegreeOfParallelism para definir un número máximo de tareas concurrentes.

Límites de protección de servicio

Una de las tres facetas supervisadas para los límites de protección del servicio es la cantidad de solicitudes simultáneas. Por defecto este valor es 52, pero puede ser mayor. Se devolverá un error si se supera el límite. Si depende del valor del encabezado de respuesta x-ms-dop-hint para limitar el grado de paralelismo, rara vez debería alcanzar este límite. Si encuentra este error, debe reducir la cantidad de subprocesos simultáneos.

Se devuelve un error específico cuando se alcanza este límite:

Código de error Código hexadecimal Publicación
-2147015898 0x80072326 Number of concurrent requests exceeded the limit of 52.

También puede mitigar la probabilidad de que ocurra este error enviando sus solicitudes a todos los servidores que admiten el entorno al deshabilitar la afinidad del servidor.

Afinidad del servidor

Cuando realiza una conexión a un servicio en Azure, se devuelve una cookie con la respuesta y todas sus solicitudes posteriores intentarán enrutarse al mismo servidor a menos que la administración de capacidad lo obligue a ir a otro servidor. Las aplicaciones cliente interactivas, especialmente los clientes de navegador, se benefician de esto porque permite reutilizar los datos almacenados en caché en el servidor. Los navegadores web siempre tienen habilitada la afinidad del servidor y no se puede deshabilitar.

Al enviar solicitudes en paralelo desde la aplicación cliente, puede obtener beneficios de rendimiento al deshabilitar esta cookie. Cada solicitud que envíe se enrutará a cualquiera de los servidores elegibles. Esto no solo aumenta el rendimiento total, sino que también ayuda a reducir el impacto de los límites de protección del servicio porque cada límite se aplica por servidor.

Los siguientes son algunos ejemplos que muestran cómo deshabilitar la afinidad del servidor con .NET.

Si está utilizando las clases ServiceClient o CrmServiceClient, agregue lo siguiente al nodo AppSettings del archivo App.config.

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

También puede establecer el valor de la propiedad EnableAffinityCookie con ServiceClient o CrmServiceClient

Esto también se puede configurar usando el constructor ServiceClient(ConnectionOptions, Boolean, ConfigurationOptions) usando la propiedad ConfigurationOptions.EnableAffinityCookie.

Optimizar la conexión

Cuando utilice .NET y envíe solicitudes en paralelo, aplique cambios de configuración como los siguientes para que sus solicitudes no estén limitadas por la configuración predeterminada:

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

Esto establece la cantidad mínima de subprocesos que crea el grupo de subprocesos a demanda, a medida que se realizan nuevas solicitudes, antes de cambiar a un algoritmo para administrar la creación y destrucción de subprocesos.

De forma predeterminada, el número mínimo de subprocesos se establece en el número de procesadores. Puede usar SetMinThreads para aumentar la cantidad mínima de subprocesos, por ejemplo, para solucionar temporalmente problemas en los que algunos elementos de trabajo o tareas en cola bloquean subprocesos del grupo de subprocesos. Esos bloqueos a veces conducen a una situación en la que todos los subprocesos de trabajo o de finalización de E/S están bloqueados (inanición). Sin embargo, aumentar la cantidad mínima de subprocesos podría degradar el rendimiento de otras maneras.

Los números que debe usar pueden variar según el hardware. Los números que use serían más bajos para una función de Azure basada en el consumo que para el código que se ejecuta en un host dedicado con hardware de gama alta.

Más información: System.Threading.ThreadPool.SetMinThreads

Configuración de System.Net.ServicePointManager

Con .NET Framework, ServicePointManager es una clase estática que se utiliza para crear, mantener y eliminar instancias de la clase ServicePoint. Utilice esta configuración con las clases ServiceClient o CrmServiceClient. Esta configuración también debería aplicarse cuando se usa HttpClient con API web en .NET Framework, pero con .NET Core, Microsoft recomienda la configuración en HttpClient en su lugar.

Límite de conexión predeterminado

Este valor está limitado en última instancia por el hardware. Si se establece demasiado alto, otros medios lo pueden sofocar. El punto clave es que debe elevarse por encima del valor predeterminado y al menos igual a la cantidad de solicitudes simultáneas que desea enviar.

Con .NET Core usando HttpClient, esto está controlado por HttpClientHandler.MaxConnectionsPerServer y el valor predeterminado es int.MaxValue.

Más información:

Expect100Continue

Cuando esta propiedad se establece en triue, el cliente esperará a que un viaje de ida y vuelta confirme una conexión con el servidor. Para HttpClient, el valor predeterminado de HttpRequestHeaders.ExpectContinue es falso.

Más información:

UseNagleAlgorithm

El algoritmo de Nagle se utiliza para reducir el tráfico de la red almacenando en búfer pequeños paquetes de datos y transmitiéndolos como un solo paquete. Este proceso también se conoce como "nagling"; se usa ampliamente porque reduce la cantidad de paquetes transmitidos y reduce la sobrecarga por paquete. Establecer esto en falso puede disminuir la sobrecarga de transmisión general, pero puede provocar un retraso en la llegada del paquete de datos.

Más información: System.Net.ServicePointManager.UseNagleAlgorithm

Ejemplos

Los siguientes ejemplos de .NET muestran el uso de Biblioteca paralela de tareas (TPL) con Dataverse.

El valor de respuesta x-ms-dop-hint está disponible a través de la propiedad RecommendedDegreesOfParallelism en ServiceClient o CrmServiceClient. Debe usar este valor al configurar ParallelOptions.MaxDegreeOfParallelism cuando usa Parallel.ForEach.

Estos ejemplos también muestran cómo establecer la propiedad EnableAffinityCookie en false.

En los siguientes ejemplos, los valores de identificación de las respuestas se agregan a un ConcurrentBag de Guids. ConcurrentBag proporciona una colección desordenada de objetos segura para subprocesos cuando el orden no importa. No se puede esperar que el orden de los Guid devueltos por este método coincida con el orden de los elementos enviados en el parámetro entityList.

Uso de ServiceClient con .NET 6 o superior

Con .NET 6 o superior, puede usar el método Parallel.ForEachAsync con los métodos asincrónicos incluidos con ServiceClient, como 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();
}

Uso de CrmServiceClient con .NET Framework

Cuando se utiliza .NET Framework, el método Clone disponible en CrmServiceClient permite duplicar una conexión existente a Dataverse para que pueda usar el método 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();
}

Consulte también

Límites de la API de protección de servicio
Ejemplo de operaciones en paralelo de la API web WebApiService (C#)
Ejemplo de operaciones paralelas de API web con componentes de flujo de datos TPL (C#)
Ejemplo: Biblioteca paralela de tareas con CrmServiceClient

Nota

¿Puede indicarnos sus preferencias de idioma de documentación? Realice una breve encuesta. (tenga en cuenta que esta encuesta está en inglés)

La encuesta durará unos siete minutos. No se recopilan datos personales (declaración de privacidad).