Guía de solicitudes limitadas en Azure Resource Graph
Al crear datos mediante programación y de uso frecuente Azure Resource Graph, se debe tener en cuenta cómo la limitación afectará a los resultados de las consultas. Cambiar la manera en que se solicitan los datos puede ayudar a evitar la limitación de la organización y mantener el flujo de datos puntuales en relación con los recursos de Azure.
En este artículo se describen cuatro áreas y patrones relacionados con la creación de consultas en Azure Resource Graph:
- Descripción de los encabezados de limitación.
- Agrupación de consultas.
- Escalonamiento de las consultas.
- Efecto de la paginación.
Descripción de los encabezados de limitación
Azure Resource Graph asigna un número de cuota para cada usuario en función de una ventana de tiempo. Por ejemplo, un usuario puede enviar como máximo 15 consultas dentro de cada ventana de 5 segundos sin que se vea limitado. El valor de la cuota se determina según varios factores y está sujeto a cambios.
En cada respuesta de consulta, Azure Resource Graph agrega dos encabezados de limitación:
x-ms-user-quota-remaining
(int): la cuota de recurso restante para el usuario. Este valor se asigna al número de consultas.x-ms-user-quota-resets-after
(hh:mm:ss): el tiempo transcurrido hasta que se restablece el consumo de la cuota de un usuario.
Cuando una entidad de seguridad tiene acceso a más de 10 000 suscripciones en el ámbito de consulta del inquilino o del grupo de administración, la respuesta se limita a las primeras 10 000 suscripciones y el encabezado x-ms-tenant-subscription-limit-hit
se devuelve como true
.
Para ilustrar cómo funcionan los encabezados, echemos un vistazo a una respuesta de consulta que tiene el encabezado y los valores de x-ms-user-quota-remaining: 10
y x-ms-user-quota-resets-after: 00:00:03
.
- Dentro de los próximos 3 segundos, se pueden enviar como máximo 10 consultas sin que se sufra una limitación.
- En 3 segundos, los valores de
x-ms-user-quota-remaining
yx-ms-user-quota-resets-after
se restablecerán a15
y00:00:05
, respectivamente.
Para ver un ejemplo del uso de los encabezados para el retroceso de las solicitudes de consulta, consulte el ejemplo en Consulta en paralelo.
Agrupación de consultas
La agrupación de consultas según la suscripción, grupo de recursos o recurso individual es más eficaz que realizar consultas en paralelo. El coste de la cuota de una consulta de mayor tamaño con frecuencia es menor que el coste de la cuota de muchas consultas pequeñas y dirigidas. Se recomienda que el tamaño del grupo sea inferior a 300.
Ejemplo de un método mal optimizado.
// NOT RECOMMENDED var header = /* your request header */ var subscriptionIds = /* A big list of subscriptionIds */ foreach (var subscriptionId in subscriptionIds) { var userQueryRequest = new QueryRequest( subscriptions: new[] { subscriptionId }, query: "Resources | project name, type"); var azureOperationResponse = await this.resourceGraphClient .ResourcesWithHttpMessagesAsync(userQueryRequest, header) .ConfigureAwait(false); // ... }
Ejemplo de un método de agrupación optimizado.
// RECOMMENDED var header = /* your request header */ var subscriptionIds = /* A big list of subscriptionIds */ const int groupSize = 100; for (var i = 0; i <= subscriptionIds.Count / groupSize; ++i) { var currSubscriptionGroup = subscriptionIds.Skip(i * groupSize).Take(groupSize).ToList(); var userQueryRequest = new QueryRequest( subscriptions: currSubscriptionGroup, query: "Resources | project name, type"); var azureOperationResponse = await this.resourceGraphClient .ResourcesWithHttpMessagesAsync(userQueryRequest, header) .ConfigureAwait(false); // ... }
Ejemplo de un método de agrupación optimizado para obtener varios recursos en una consulta.
Resources | where id in~ ({resourceIdGroup}) | project name, type
// RECOMMENDED var header = /* your request header */ var resourceIds = /* A big list of resourceIds */ const int groupSize = 100; for (var i = 0; i <= resourceIds.Count / groupSize; ++i) { var resourceIdGroup = string.Join(",", resourceIds.Skip(i * groupSize).Take(groupSize).Select(id => string.Format("'{0}'", id))); var userQueryRequest = new QueryRequest( subscriptions: subscriptionList, query: $"Resources | where id in~ ({resourceIdGroup}) | project name, type"); var azureOperationResponse = await this.resourceGraphClient .ResourcesWithHttpMessagesAsync(userQueryRequest, header) .ConfigureAwait(false); // ... }
Escalonamiento de las consultas
Debido a la manera en que se aplica la limitación, se recomienda que las consultas se escalonen. Es decir, en lugar de enviar 60 consultas al mismo tiempo, escalone las consultas en cuatro ventanas de 5 segundos.
Programación de consultas no escalonadas.
Número de consultas 60 0 0 0 Intervalo de tiempo (s) 0-5 5-10 10-15 15-20 Programación de consultas escalonadas.
Número de consultas 15 15 15 15 Intervalo de tiempo (s) 0-5 5-10 10-15 15-20
El código siguiente es un ejemplo de respetar los encabezados de limitación al consultar Azure Resource Graph.
while (/* Need to query more? */)
{
var userQueryRequest = /* ... */
// Send post request to Azure Resource Graph
var azureOperationResponse = await this.resourceGraphClient
.ResourcesWithHttpMessagesAsync(userQueryRequest, header)
.ConfigureAwait(false);
var responseHeaders = azureOperationResponse.response.Headers;
int remainingQuota = /* read and parse x-ms-user-quota-remaining from responseHeaders */
TimeSpan resetAfter = /* read and parse x-ms-user-quota-resets-after from responseHeaders */
if (remainingQuota == 0)
{
// Need to wait until new quota is allocated
await Task.Delay(resetAfter).ConfigureAwait(false);
}
}
Consulta en paralelo
Aunque la agrupación es recomendable en comparación con la paralelización, hay ocasiones en las que las consultas no se pueden agrupar fácilmente. En estos casos, puede consultar Azure Resource Graph al enviar varias consultas en paralelo. En el ejemplo siguiente se muestra cómo revertir en función de los encabezados de limitación.
IEnumerable<IEnumerable<string>> queryGroup = /* Groups of queries */
// Run groups in parallel.
await Task.WhenAll(queryGroup.Select(ExecuteQueries)).ConfigureAwait(false);
async Task ExecuteQueries(IEnumerable<string> queries)
{
foreach (var query in queries)
{
var userQueryRequest = new QueryRequest(
subscriptions: subscriptionList,
query: query);
// Send post request to Azure Resource Graph.
var azureOperationResponse = await this.resourceGraphClient
.ResourcesWithHttpMessagesAsync(userQueryRequest, header)
.ConfigureAwait(false);
var responseHeaders = azureOperationResponse.response.Headers;
int remainingQuota = /* read and parse x-ms-user-quota-remaining from responseHeaders */
TimeSpan resetAfter = /* read and parse x-ms-user-quota-resets-after from responseHeaders */
if (remainingQuota == 0)
{
// Delay by a random period to avoid bursting when the quota is reset.
var delay = (new Random()).Next(1, 5) * resetAfter;
await Task.Delay(delay).ConfigureAwait(false);
}
}
}
Paginación
Dado que Azure Resource Graph devuelve un máximo de 1000 entradas en una única respuesta de consulta, es posible que tenga que paginar las consultas para obtener el conjunto de datos completo que desee. Sin embargo, algunos clientes de Azure Resource Graph controlan la paginación de forma diferente a otros.
Al usar el SDK de ResourceGraph, tendrá que controlar la paginación al pasar el token de omisión que se devuelve de la respuesta de la consulta anterior a la siguiente consulta paginada. Este diseño significa que necesita recopilar los resultados de todas las llamadas paginadas y combinarlos al final. En este caso, cada consulta paginada que envíe toma una cuota de consulta.
var results = new List<object>();
var queryRequest = new QueryRequest(
subscriptions: new[] { mySubscriptionId },
query: "Resources | project id, name, type");
var azureOperationResponse = await this.resourceGraphClient
.ResourcesWithHttpMessagesAsync(queryRequest, header)
.ConfigureAwait(false);
while (!string.IsNullOrEmpty(azureOperationResponse.Body.SkipToken))
{
queryRequest.Options ??= new QueryRequestOptions();
queryRequest.Options.SkipToken = azureOperationResponse.Body.SkipToken;
var azureOperationResponse = await this.resourceGraphClient
.ResourcesWithHttpMessagesAsync(queryRequest, header)
.ConfigureAwait(false);
results.Add(azureOperationResponse.Body.Data.Rows);
// Inspect throttling headers in query response and delay the next call if needed.
}
¿Todavía se está limitando?
Si ha usado las recomendaciones de este artículo y las consultas de Azure Resource Graph se siguen limitando, póngase en contacto con el equipo de Azure Resource Graph. El equipo proporciona asistencia para Azure Resource Graph, pero no para la limitación de Microsoft Graph.
Proporcione estos detalles al ponerse en contacto con el equipo de Azure Resource Graph:
- Sus necesidades de controlador de casos prácticos y empresariales de un límite superior.
- ¿A cuántos recursos tiene acceso? ¿Cuántos de ellos se devuelven desde una consulta única?
- ¿Qué tipos de recursos le interesa?
- ¿Cuál es el modelo de consulta? X consultas por Y segundo, etc.
Pasos siguientes
- Vea el lenguaje en uso enConsultas básicas.
- Vea los usos avanzados en Consultas avanzadas.
- Obtenga más información sobre cómo explorar recursos.