Richtlijnen voor beperkte aanvragen in Azure Resource Graph

Bij het maken van programmatisch en frequent gebruik van Azure Resource Graph-gegevens moet u rekening houden met hoe beperking van invloed is op de resultaten van de query's. Als u de manier wijzigt waarop gegevens worden aangevraagd, kunnen u en uw organisatie voorkomen dat ze worden beperkt en de stroom van tijdige gegevens over uw Azure-resources behouden.

In dit artikel worden vier gebieden en patronen behandeld die betrekking hebben op het maken van query's in Azure Resource Graph:

  • Informatie over beperkingsheaders.
  • Query's groeperen.
  • Verspringende query's.
  • Het effect van paginering.

Informatie over headers voor bandbreedtebeperking

Azure Resource Graph wijst elke gebruiker een quotumnummer toe op basis van een tijdvenster. Een gebruiker kan bijvoorbeeld maximaal 15 query's verzenden binnen elk venster van 5 seconden zonder te worden beperkt. De quotumwaarde wordt bepaald door veel factoren en kan worden gewijzigd.

In elk queryantwoord voegt Azure Resource Graph twee beperkingsheaders toe:

  • x-ms-user-quota-remaining (int): Het resterende resourcequotum voor de gebruiker. Deze waarde wordt toegewezen aan het aantal query's.
  • x-ms-user-quota-resets-after (uu:mm:ss): de tijdsduur totdat het quotumverbruik van een gebruiker opnieuw wordt ingesteld.

Wanneer een beveiligingsprincipaal toegang heeft tot meer dan 10.000 abonnementen binnen het querybereik van de tenant of beheergroep, is het antwoord beperkt tot de eerste 10.000 abonnementen en wordt de x-ms-tenant-subscription-limit-hit header geretourneerd als true.

Laten we eens kijken naar een queryantwoord met de koptekst en waarden van x-ms-user-quota-remaining: 10 en x-ms-user-quota-resets-after: 00:00:03om te laten zien hoe de headers werken.

  • Binnen de volgende 3 seconden kunnen maximaal 10 query's worden verzonden zonder te worden beperkt.
  • In 3 seconden worden de waarden van x-ms-user-quota-remaining en x-ms-user-quota-resets-after respectievelijk opnieuw ingesteld 15 op en 00:00:05 respectievelijk.

Als u een voorbeeld wilt zien van het gebruik van de headers voor uitstel van queryaanvragen, raadpleegt u het voorbeeld in Query parallel.

Groeperen van query's

Het groeperen van query's per abonnement, resourcegroep of afzonderlijke resource is efficiënter dan het parallelliseren van query's. De quotumkosten van een grotere query zijn vaak lager dan de quotumkosten van veel kleine en gerichte query's. De groepsgrootte wordt aanbevolen om kleiner dan 300 te zijn.

  • Voorbeeld van een slecht geoptimaliseerde benadering.

    // 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: "Resoures | project name, type");
    
        var azureOperationResponse = await this.resourceGraphClient
            .ResourcesWithHttpMessagesAsync(userQueryRequest, header)
            .ConfigureAwait(false);
    
    // ...
    }
    
  • Voorbeeld van een geoptimaliseerde groeperingsmethode.

    // 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);
    
      // ...
    }
    
  • Voorbeeld van een geoptimaliseerde groeperingsmethode voor het verkrijgen van meerdere resources in één query.

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

Query's spreiden

Vanwege de manier waarop beperking wordt afgedwongen, wordt u aangeraden query's te spreiden. In plaats van bijvoorbeeld 60 query's tegelijk te verzenden, kunt u de query's in vier vensters van 5 seconden verdelen.

  • Niet-getaggered queryschema.

    Aantal query's 60 0 0 0
    Tijdsinterval (sec) 0-5 5-10 10-15 15-20
  • Gefaseerde queryplanning.

    Aantal query's 15 15 15 15
    Tijdsinterval (sec) 0-5 5-10 10-15 15-20

De volgende code is een voorbeeld van het respecteren van beperkingsheaders bij het uitvoeren van query's op 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);
    }
}

Query parallel uitvoeren

Hoewel groepering wordt aanbevolen voor parallelle uitvoering, zijn er situaties waarin query's niet eenvoudig kunnen worden gegroepeerd. In dergelijke gevallen wilt u mogelijk query's uitvoeren op Azure Resource Graph door meerdere query's parallel te verzenden. In het volgende voorbeeld ziet u hoe u uitstelt op basis van beperkingsheaders.

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

Paginering

Omdat Azure Resource Graph maximaal 1000 vermeldingen retourneert in één queryantwoord, moet u mogelijk uw query's pagineren om de gewenste volledige gegevensset op te halen. Maar sommige Azure Resource Graph-clients verwerken paginering anders dan andere.

Wanneer u ResourceGraph SDK gebruikt, moet u paginering afhandelen door het skip-token door te geven dat wordt geretourneerd vanuit het vorige queryantwoord naar de volgende gepagineerde query. Dit ontwerp betekent dat u resultaten van alle gepagineerde oproepen moet verzamelen en deze aan het einde moet combineren. In dit geval heeft elke gepagineerde query die u verzendt één queryquotum.

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.
}

Nog steeds beperkt?

Als u de aanbevelingen van dit artikel hebt gebruikt en uw Azure Resource Graph-query's nog steeds worden beperkt, neemt u contact op met het Azure Resource Graph-team. Het team ondersteunt Azure Resource Graph, maar biedt geen ondersteuning voor Microsoft Graph-beperking.

Geef deze gegevens op wanneer u contact op neemt met het Azure Resource Graph-team:

  • Uw specifieke use-case- en business driver-behoeften voor een hogere beperkingslimiet.
  • Tot hoeveel resources hebt u toegang? Hoeveel van de query's worden geretourneerd uit één query?
  • In welke typen resources bent u geïnteresseerd?
  • Wat is uw querypatroon? X-query's per Y seconden, enzovoort.

Volgende stappen