Pokyny k omezování požadavků v Azure Resource Graphu

Při vytváření programových a častých použití dat Azure Resource Graphu je potřeba vzít v úvahu, jak omezování ovlivňuje výsledky dotazů. Změna způsobu vyžádání dat vám a vaší organizaci může pomoct vyhnout se omezování a udržovat tok včasných dat o vašich prostředcích Azure.

Tento článek se zabývá čtyřmi oblastmi a vzory souvisejícími s vytvářením dotazů v Azure Resource Graphu:

  • Seznamte se s hlavičkami omezování.
  • Seskupování dotazů
  • Prohánějí se dotazy.
  • Účinek stránkování.

Vysvětlení hlaviček omezování

Azure Resource Graph každému uživateli přidělí číslo kvóty na základě časového intervalu. Uživatel může například posílat maximálně 15 dotazů v každém 5sekundovém okně bez omezení. Hodnota kvóty je určena mnoha faktory a může se změnit.

Do každé odpovědi na dotaz Azure Resource Graph přidá dvě hlavičky omezování:

  • x-ms-user-quota-remaining (int): Zbývající kvóta prostředků pro uživatele. Tato hodnota se mapuje na počet dotazů.
  • x-ms-user-quota-resets-after (hh:mm:ss): Doba trvání do resetování využití kvóty uživatele.

Pokud má objekt zabezpečení přístup k více než 10 000 předplatným v rámci oboru dotazu tenanta nebo skupiny pro správu, odpověď je omezená na prvních 10 000 předplatných a hlavička x-ms-tenant-subscription-limit-hit se vrátí jako true.

Abychom si ukázali, jak záhlaví fungují, podívejme se na odpověď dotazu, která obsahuje hlavičku a hodnoty x-ms-user-quota-remaining: 10 a x-ms-user-quota-resets-after: 00:00:03.

  • Během dalších 3 sekund je možné odeslat maximálně 10 dotazů bez omezení.
  • Za 3 sekundy se hodnoty x-ms-user-quota-remaining a x-ms-user-quota-resets-after resetují se do 15 a 00:00:05 v uvedeném pořadí.

Pokud chcete vidět příklad použití hlaviček k vrácení požadavků na dotazy, podívejte se na ukázku paralelně v dotazu.

Seskupování dotazů

Seskupení dotazů podle předplatného, skupiny prostředků nebo jednotlivých prostředků je efektivnější než paralelizace dotazů. Využití kvóty u větších dotazů je často nižší než využití kvóty u mnoha malých cílených dotazů. Velikost skupiny se doporučuje menší než 300.

  • Příklad špatně optimalizovaného přístupu

    // 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);
    
    // ...
    }
    
  • Příklad optimalizovaného přístupu seskupení

    // 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);
    
      // ...
    }
    
  • Příklad optimalizovaného přístupu seskupení pro získání více prostředků v jednom dotazu

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

Rozložení dotazů

Vzhledem ke způsobu vynucování omezování doporučujeme dotazy rozkládat. Například místo odesílání 60 dotazů najednou můžete dotazy rozdělit do čtyř 5sekundových oken.

  • Neplánovaný plán dotazů.

    Počet dotazů 60 0 0 0
    Časový interval (s) 0-5 5-10 10-15 15-20
  • Rozsaděný plán dotazů

    Počet dotazů 15 15 15 15
    Časový interval (s) 0-5 5-10 10-15 15-20

Následující kód je příkladem dodržování hlaviček omezování při dotazování azure Resource Graphu.

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

Paralelní dotazování

I když se seskupování doporučuje přes paralelizaci, někdy se dotazy nedají snadno seskupit. V těchto případech můžete chtít dotazovat Azure Resource Graph odesláním více dotazů paralelně. Následující příklad ukazuje, jak se vrátit zpět na základě hlaviček omezová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);
        }
    }
}

Stránkování

Vzhledem k tomu, že Azure Resource Graph vrací maximálně 1 000 položek v jedné odpovědi dotazu, budete možná muset stránkovat dotazy, abyste získali požadovanou datovou sadu. Někteří klienti Azure Resource Graphu ale zpracovávají stránkování jinak než ostatní.

Při použití sady ResourceGraph SDK je potřeba zpracovat stránkování předáním tokenu přeskočení vráceného z předchozí odpovědi dotazu na další stránkovaný dotaz. Tento návrh znamená, že potřebujete shromáždit výsledky ze všech stránkovaných volání a zkombinovat je na konci. V tomto případě má každý stránkovaný dotaz, který odešlete, jednu kvótu dotazu.

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

Stále dochází k omezování?

Pokud jste použili doporučení tohoto článku a dotazy Azure Resource Graphu se stále omezují, obraťte se na tým Azure Resource Graphu. Tým podporuje Azure Resource Graph, ale nepodporuje omezování Microsoft Graphu.

Při kontaktování týmu Azure Resource Graphu zadejte tyto podrobnosti:

  • Váš konkrétní případ použití a obchodní faktor potřebují vyšší limit omezování.
  • K kolika prostředkům máte přístup? Kolik z nich se vrátí z jednoho dotazu?
  • Jaké typy prostředků vás zajímají?
  • Jaký je vzor dotazu? X dotazy za sekundu Y atd.

Další kroky