Wskazówki dotyczące żądań ograniczonych w usłudze Azure Resource Graph

Podczas tworzenia programowego i częstego używania danych usługi Azure Resource Graph należy wziąć pod uwagę, jak ograniczanie wpływa na wyniki zapytań. Zmiana sposobu żądania danych może pomóc Tobie i Twojej organizacji uniknąć ograniczania przepustowości i utrzymywać przepływ terminowych danych dotyczących zasobów platformy Azure.

W tym artykule opisano cztery obszary i wzorce związane z tworzeniem zapytań w usłudze Azure Resource Graph:

  • Omówienie nagłówków ograniczania przepustowości.
  • Grupowanie zapytań.
  • Zdumiewające zapytania.
  • Efekt stronicowania.

Omówienie nagłówków ograniczeń

Usługa Azure Resource Graph przydziela numer limitu przydziału dla każdego użytkownika na podstawie przedziału czasu. Na przykład użytkownik może wysyłać co najwyżej 15 zapytań w co 5-sekundowym oknie bez ograniczania przepustowości. Wartość limitu przydziału jest określana przez wiele czynników i może ulec zmianie.

W każdej odpowiedzi zapytania usługa Azure Resource Graph dodaje dwa nagłówki ograniczania:

  • x-ms-user-quota-remaining (int): pozostały limit przydziału zasobów dla użytkownika. Ta wartość jest mapowania na liczbę zapytań.
  • x-ms-user-quota-resets-after (hh:mm:ss): czas trwania do momentu zresetowania użycia limitu przydziału użytkownika.

Jeśli podmiot zabezpieczeń ma dostęp do ponad 10 000 subskrypcji w zakresie zapytania dzierżawy lub grupy zarządzania, odpowiedź jest ograniczona do pierwszych 10 000 subskrypcji, a x-ms-tenant-subscription-limit-hit nagłówek jest zwracany jako true.

Aby zilustrować działanie nagłówków, przyjrzyjmy się odpowiedzi na zapytanie zawierającej nagłówki x-ms-user-quota-remaining: 10 i wartości i x-ms-user-quota-resets-after: 00:00:03.

  • W ciągu najbliższych 3 sekund można przesłać co najwyżej 10 zapytań bez ograniczania.
  • W ciągu 3 sekund wartości x-ms-user-quota-remaining i są odpowiednio resetowane do 15 i x-ms-user-quota-resets-after00:00:05.

Aby zobaczyć przykład użycia nagłówków do wycofywania żądań zapytań, zobacz przykład w zapytaniu równoległym.

Grupowanie zapytań

Grupowanie zapytań według subskrypcji, grupy zasobów lub pojedynczego zasobu jest bardziej wydajne niż zapytania równoległe. Koszt limitu przydziału większego zapytania jest często mniejszy niż koszt limitu przydziału wielu małych i docelowych zapytań. Zaleca się, aby rozmiar grupy był mniejszy niż 300.

  • Przykład źle zoptymalizowanego podejścia.

    // 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);
    
    // ...
    }
    
  • Przykład zoptymalizowanego podejścia do grupowania.

    // 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);
    
      // ...
    }
    
  • Przykład zoptymalizowanego podejścia do grupowania w celu uzyskania wielu zasobów w jednym zapytaniu.

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

Rozkładanie zapytań

Ze względu na sposób wymuszania ograniczania zalecamy, aby zapytania zostały rozłożone. Na przykład zamiast wysyłać 60 zapytań w tym samym czasie, można podzielić zapytania na cztery 5-sekundowe okna.

  • Niezrównany harmonogram zapytań.

    Liczba zapytań 60 0 0 0
    Interwał czasu (s) 0-5 5-10 10-15 15-20
  • Rozsyłany harmonogram zapytań.

    Liczba zapytań 15 15 15 15
    Interwał czasu (s) 0-5 5-10 10-15 15-20

Poniższy kod to przykład przestrzegania nagłówków ograniczania przepustowości podczas wykonywania zapytań względem usługi 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);
    }
}

Równoległe wykonywanie zapytań

Mimo że grupowanie jest zalecane w przypadku równoległości, czasami nie można łatwo grupować zapytań. W takich przypadkach możesz chcieć wysłać zapytanie do usługi Azure Resource Graph, wysyłając wiele zapytań w sposób równoległy. W poniższym przykładzie pokazano, jak wycofać się na podstawie nagłówków ograniczania przepustowości.

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

Podział na strony

Ponieważ usługa Azure Resource Graph zwraca maksymalnie 1000 wpisów w jednej odpowiedzi na zapytanie, może być konieczne stronicowanie zapytań w celu uzyskania kompletnego zestawu danych. Jednak niektórzy klienci usługi Azure Resource Graph obsługują stronicowanie inaczej niż inne.

W przypadku korzystania z zestawu ResourceGraph SDK należy obsługiwać stronicowanie, przekazując token pomijania zwracany z poprzedniej odpowiedzi zapytania na następne zapytanie podzielone na strony. Ten projekt oznacza, że musisz zebrać wyniki ze wszystkich wywołań podzielonych na strony i połączyć je razem na końcu. W takim przypadku każde wysyłane zapytanie podzielone na strony przyjmuje jeden limit przydziału zapytania.

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

Nadal ograniczane?

Jeśli użyto zaleceń tego artykułu, a zapytania usługi Azure Resource Graph są nadal ograniczane, skontaktuj się z zespołem usługi Azure Resource Graph. Zespół obsługuje usługę Azure Resource Graph, ale nie obsługuje ograniczania usługi Microsoft Graph.

Podaj te szczegóły podczas kontaktu z zespołem usługi Azure Resource Graph:

  • Określony przypadek użycia i czynnik biznesowy wymaga zwiększenia limitu ograniczania przepustowości.
  • Ilu zasobów masz dostęp? Ile z tych elementów jest zwracanych z pojedynczego zapytania?
  • Jakie typy zasobów cię interesują?
  • Jaki jest wzorzec zapytania? X zapytań na Y sekund i tak dalej.

Następne kroki

  • Zobacz język używany w zapytaniach startowych.
  • Zobacz zaawansowane zastosowania w zapytaniach zaawansowanych.
  • Dowiedz się więcej o sposobie eksplorowania zasobów.