Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Cuando la aplicación necesita enviar un gran número de solicitudes a Dataverse, puede lograr un rendimiento total mucho mayor mediante el envío de solicitudes en paralelo mediante el uso de 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. Para obtener más información, consulte No usar la ejecución en paralelo dentro de complementos y actividades de flujo de trabajo.
Grado de paralelismo óptimo (DOP)
Dataverse administra la asignación de recursos para entornos. Entornos de producción, que muchos usuarios con licencia usan, tienen más recursos asignados. El número y las funcionalidades de los servidores asignados pueden variar con el tiempo, por lo que no hay ningún número fijo para 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 se usa la programación paralela 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 el mejor ajuste para el entorno, puede que esté enviando demasiadas solicitudes. Establezca la propiedad ParallelOptions.MaxDegreeOfParallelism para definir un número máximo de tareas simultáneas.
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. De forma predeterminada, este valor es 52, pero puede ser mayor. Se devuelve un error si se supera el límite. Si depende del valor de la cabecera de respuesta x-ms-dop-hint para limitar el grado de paralelismo, debería rara vez alcanzar este límite. Si se produce este error, reduzca el número de subprocesos simultáneos.
Se devuelve un error específico cuando se alcanza este límite:
| Código de error | Código hexadecimal | Mensaje |
|---|---|---|
-2147015898 |
0x80072326 |
Number of concurrent requests exceeded the limit of 52. |
También puede reducir la probabilidad de este error enviando las solicitudes a todos los servidores que admiten el entorno deshabilitando la afinidad de servidor.
Afinidad del servidor
Cuando se conecta a un servicio en Azure, el servicio devuelve una cookie con la respuesta. Todas las solicitudes posteriores intentan ir al mismo servidor a menos que la administración de capacidad obliga a la solicitud a ir a otro servidor. Las aplicaciones cliente interactivas, especialmente los clientes del explorador, se benefician de esta cookie porque permite a la aplicación reutilizar los datos almacenados en caché en el servidor. Los exploradores web siempre tienen habilitada la afinidad de servidor y no se puede deshabilitar.
Al enviar solicitudes en paralelo desde la aplicación cliente, puede obtener ventajas de rendimiento deshabilitando esta cookie. Cada solicitud que envíe se redirige a uno de los servidores aptos. Este cambio no solo aumenta el rendimiento total, sino que también ayuda a reducir el impacto de los límites de protección de servicio porque cada límite se aplica por servidor.
En los ejemplos siguientes se muestra cómo deshabilitar la afinidad de servidor mediante .NET.
Si usa las clases ServiceClient o CrmServiceClient , agregue el código siguiente al AppSettings nodo del App.config archivo.
<add key="PreferConnectionAffinity" value="false" />
También puede establecer el valor de la EnableAffinityCookie propiedad mediante las clases ServiceClient o CrmServiceClient .
También puede establecer esta propiedad mediante el constructor ServiceClient(ConnectionOptions, Boolean, ConfigurationOptions) y la propiedad ConfigurationOptions.EnableAffinityCookie .
Optimizar la conexión
Cuando use .NET y envíe solicitudes en paralelo, cambie la configuración predeterminada para que las solicitudes no estén limitadas por ellas. Se han realizado los siguientes cambios:
// 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
Esta configuración controla el número mínimo de subprocesos que el grupo de subprocesos crea a petición a medida que entran nuevas solicitudes. Después de alcanzar este número, el grupo de subprocesos cambiará a un algoritmo encargado de gestionar 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. Use SetMinThreads para aumentar el número mínimo de subprocesos. Por ejemplo, puede aumentar temporalmente el número para solucionar problemas en los que algunos elementos de trabajo en cola o tareas obstruyen los hilos del grupo de hilos. 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 use pueden variar según el hardware. Los números que utilizas son 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 alta gama.
Más información: System.Threading.ThreadPool.SetMinThreads
Configuración de System.Net.ServicePointManager
En .NET Framework, ServicePointManager es una clase estática que se usa 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 se debe aplicar al usar HttpClient con la API web en .NET Framework. Sin embargo, con .NET Core, Microsoft recomienda usar la configuración en HttpClient en su lugar.
Límite de conexión predeterminado
En última instancia, el hardware limita este valor. Si lo establece demasiado alto, otros mecanismos lo regulan. Aumente por encima del valor predeterminado y establézcalo al menos igual al número de solicitudes simultáneas que quiere enviar.
Cuando se usa .NET Core con HttpClient, la propiedad HttpClientHandler.MaxConnectionsPerServer controla esta configuración. Su valor predeterminado es int. MaxValue.
Para obtener más información, consulte:
- System.Net.ServicePointManager.DefaultConnectionLimit
- Límites del grupo de conexiones de .NET Framework y el nuevo SDK de Azure para .NET
- Configuración de ServicePointManager para WebJobs
- HttpClientHandler.MaxConnectionsPerServer
Expect100Continue
Al establecer esta propiedad en true, el cliente espera una confirmación de ida y vuelta de una conexión al servidor. Para HttpClient, el valor predeterminado de HttpRequestHeaders.ExpectContinue es false.
Para obtener más información, consulte:
UseNagleAlgorithm
El algoritmo nagle reduce el tráfico de red almacenando en búfer pequeños paquetes de datos y transmitiéndolos como un único paquete. Este proceso también se conoce como "nagling". Se usa ampliamente porque disminuye el número de paquetes transmitidos y reduce la sobrecarga por paquete. Establecer este valor en false puede disminuir la sobrecarga general de transmisión, pero puede provocar un retraso en la llegada de paquetes de datos.
Para obtener más información, consulte System.Net.ServicePointManager.UseNagleAlgorithm.
Ejemplos
En los siguientes ejemplos de .NET se muestra cómo usar la biblioteca paralela de tareas (TPL) con Dataverse.
Puede acceder al valor de la respuesta de x-ms-dop-hint a través de la propiedad RecommendedDegreesOfParallelism en ServiceClient o CrmServiceClient. Use este valor al establecer ParallelOptions.MaxDegreeOfParallelism al usar Parallel.ForEach.
Estos ejemplos también muestran cómo establecer la propiedad EnableAffinityCookie en false.
En los ejemplos siguientes, agregue los valores de identificador de las respuestas a ConcurrentBag de GUID.
ConcurrentBag proporciona una colección desordenada de objetos con seguridad para hilos 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 entityList parámetro .
Uso de ServiceClient con .NET 6 o superior
Al usar .NET 6 o posterior, puede usar el método Parallel.ForEachAsync con los métodos asincrónicos de 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 usa .NET Framework, el método Clone disponible en CrmServiceClient duplica una conexión existente a Dataverse para poder 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