Richtlijnen voor beperkte aanvragen in Azure Resource Graph

Bij het maken van programmatisch en frequent gebruik van Azure Resource Graph-gegevens, moet rekening worden gehouden met de invloed van beperking op de resultaten van de query's. Als u de manier wijzigt waarop gegevens worden opgevraagd, 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 met betrekking tot het maken van query's in Azure Resource Graph:

  • Informatie over headers voor bandbreedtebeperking
  • Groeperen van query's
  • Spreiden van query's
  • De impact 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 binnen elk venster van 5 seconden verzenden 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 beveiligingsprincipal toegang heeft tot meer dan 10.000 abonnementen binnen het querybereik van de tenant of beheergroep, wordt het antwoord beperkt tot de eerste 10.000 abonnementen en wordt de x-ms-tenant-subscription-limit-hit header geretourneerd als true.

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

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

Zie het voorbeeld in Query in Parallel voor een voorbeeld van het gebruik van de headers voor uitstel bij queryaanvragen.

Groeperen van query's

Het groeperen van query's op 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 1 van een geoptimaliseerde groeperingsbenadering

    // 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 2 van een geoptimaliseerde groeperingsbenadering 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 60 query's tegelijk te verzenden, kunt u de query's in vier vensters van 5 seconden verdelen:

  • Niet-gespreid queryschema

    Aantal query's 60 0 0 0
    Tijdsinterval (sec) 0-5 5-10 10-15 15-20
  • Gespreid queryschema

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

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

Parallel query uitvoeren

Hoewel groeperen wordt aanbevolen boven parallelle uitvoering, zijn er momenten waarop query's niet eenvoudig kunnen worden gegroepeerd. In dergelijke gevallen kunt u query's uitvoeren op Azure Resource Graph door meerdere query's parallel te verzenden. Hier volgt een voorbeeld van hoe u in dergelijke scenario's 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

Aangezien Azure Resource Graph maximaal 1000 vermeldingen in één queryantwoord retourneert, moet u mogelijk uw query's pagineren om de volledige gegevensset te krijgen die u zoekt. Sommige Azure Resource Graph-clients verwerken paginering echter anders dan andere.

  • C# SDK

    Wanneer u ResourceGraph SDK gebruikt, moet u paginering afhandelen door het skip-token dat wordt geretourneerd vanuit het vorige queryantwoord door te geven aan de volgende gepagineerde query. Dit ontwerp betekent dat u resultaten van alle gepagineerde aanroepen moet verzamelen en deze aan het einde samen 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 wordt beperkt nadat u de bovenstaande aanbevelingen hebt uitgevoerd, neemt u contact op met het Azure Resource Graph-team.

Geef de volgende details op:

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

Volgende stappen