Vägledning för begränsade begäranden i Azure Resource Graph

När du skapar programmatisk och frekvent användning av Azure Resource Graph-data bör du tänka på hur begränsning påverkar resultatet av frågorna. Om du ändrar hur data begärs kan du och din organisation undvika att begränsas och underhålla flödet av data i rätt tid om dina Azure-resurser.

Den här artikeln beskriver fyra områden och mönster som rör skapandet av frågor i Azure Resource Graph:

  • Förstå begränsningsrubriker.
  • Gruppera frågor.
  • Häpnadsväckande frågor.
  • Effekten av sidnumrering.

Förstå begränsningsrubriker

Azure Resource Graph allokerar ett kvotnummer för varje användare baserat på ett tidsfönster. En användare kan till exempel skicka högst 15 frågor inom varje 5-sekundersfönster utan att begränsas. Kvotvärdet bestäms av många faktorer och kan komma att ändras.

I varje frågesvar lägger Azure Resource Graph till två begränsningshuvuden:

  • x-ms-user-quota-remaining (int): Den återstående resurskvoten för användaren. Det här värdet mappar till antalet frågor.
  • x-ms-user-quota-resets-after (hh:mm:ss): Tidsperioden tills en användares kvotförbrukning återställs.

När ett säkerhetsobjekt har åtkomst till fler än 10 000 prenumerationer inom frågeomfånget för klientorganisation eller hanteringsgrupp begränsas svaret till de första 10 000 prenumerationerna och x-ms-tenant-subscription-limit-hit huvudet returneras som true.

För att illustrera hur rubrikerna fungerar ska vi titta på ett frågesvar som har huvudet och värdena x-ms-user-quota-remaining: 10 för och x-ms-user-quota-resets-after: 00:00:03.

  • Inom de närmaste 3 sekunderna kan högst 10 frågor skickas utan begränsning.
  • Om 3 sekunder återställs x-ms-user-quota-remaining värdena för och x-ms-user-quota-resets-after till 1500:00:05 respektive.

Om du vill se ett exempel på hur du använder rubrikerna för att backoffa på frågebegäranden kan du läsa exemplet i Fråga parallellt.

Gruppering av frågor

Att gruppera frågor efter prenumeration, resursgrupp eller enskild resurs är effektivare än att parallellisera frågor. Kvotkostnaden för en större fråga är ofta mindre än kvotkostnaden för många små och riktade frågor. Gruppstorleken rekommenderas att vara mindre än 300.

  • Exempel på en dåligt optimerad metod.

    // 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);
    
    // ...
    }
    
  • Exempel på en optimerad grupperingsmetod.

    // 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);
    
      // ...
    }
    
  • Exempel på en optimerad grupperingsmetod för att hämta flera resurser i en fråga.

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

Spridning av frågor

På grund av hur begränsning tillämpas rekommenderar vi att frågor sprids ut. I stället för att skicka 60 frågor samtidigt kan du till exempel flytta frågorna till fyra 5-sekundersfönster.

  • Icke-förskjutet frågeschema.

    Antal frågor 60 0 0 0
    Tidsintervall (sek) 0-5 5-10 10-15 15-20
  • Förskjutet frågeschema.

    Antal frågor 15 15 15 15
    Tidsintervall (sek) 0-5 5-10 10-15 15-20

Följande kod är ett exempel på att respektera begränsningshuvuden när du kör frågor mot 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);
    }
}

Fråga parallellt

Även om gruppering rekommenderas över parallellisering finns det tillfällen då frågor inte kan grupperas enkelt. I dessa fall kanske du vill fråga Azure Resource Graph genom att skicka flera frågor parallellt. I följande exempel visas hur du backar baserat på begränsningshuvuden.

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

Sidnumrering

Eftersom Azure Resource Graph returnerar högst 1 000 poster i ett enda frågesvar kan du behöva sidnumrera dina frågor för att få den fullständiga datamängd du vill ha. Men vissa Azure Resource Graph-klienter hanterar sidnumrering på ett annat sätt än andra.

När du använder ResourceGraph SDK måste du hantera sidnumrering genom att skicka den hopptoken som returneras från föregående frågesvar till nästa sidnumrerade fråga. Den här designen innebär att du måste samla in resultat från alla sidnumrerade anrop och kombinera dem tillsammans i slutet. I det här fallet tar varje sidnumrerad fråga som du skickar en frågekvot.

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

Håller du fortfarande på att begränsas?

Om du använde den här artikelns rekommendationer och dina Azure Resource Graph-frågor fortfarande begränsas kontaktar du Azure Resource Graph-teamet. Teamet stöder Azure Resource Graph men stöder inte Microsoft Graph-begränsning.

Ange den här informationen när du kontaktar Azure Resource Graph-teamet:

  • Ditt specifika användningsfall och affärsdrivande behov av en högre begränsningsgräns.
  • Hur många resurser har du åtkomst till? Hur många av dem returneras från en enda fråga?
  • Vilka typer av resurser är du intresserad av?
  • Vad är ditt frågemönster? X-frågor per Y-sekunder och så vidare.

Nästa steg