Leitfaden für gedrosselte Anforderungen in Azure Resource Graph

Beim Erstellen von programmgesteuerten und häufigen Verwendung von Azure Resource Graph-Daten sollte berücksichtigt werden, wie sich die Drosselung auf die Ergebnisse der Abfragen auswirkt. Indem Sie die Anforderungsweise von Daten verändern, können Sie und Ihre Organisation die Drosselung von Anforderungen vermeiden und den Flow von zeitgenauen Daten zu Ihren Azure-Ressourcen aufrechterhalten.

In diesem Artikel werden vier Bereiche und Muster behandelt, die im Zusammenhang mit der Erstellung von Abfragen in Azure Resource Graph stehen:

  • Grundlegendes zur Einschränkung von Kopfzeilen.
  • Gruppieren von Abfragen.
  • Gestaffelte Abfragen.
  • Die Auswirkung der Paginierung.

Grundlegendes zu Drosselungsheadern

Azure Resource Graph ordnet, abhängig von einem Zeitfenster, jedem Benutzer eine Kontingentnummer zu. Ein Benutzer kann innerhalb eines Zeitfensters von 5 Sekunden beispielsweise höchstens 15 Abfragen ohne Drosselung senden. Der Kontingentwert wird von vielen Faktoren bestimmt und kann verändert werden.

In jeder Abfrageantwort fügt Azure Resource Graph zwei Drosselungsheader ein:

  • x-ms-user-quota-remaining (int): Das verbleibende Ressourcenkontingent für den Benutzer. Dieser Wert entspricht der Anzahl von Abfragen.
  • x-ms-user-quota-resets-after (hh:mm:ss): Der Zeitraum, bis die Kontingentnutzung eines Benutzers zurückgesetzt wird.

Wenn ein Sicherheitsprinzipal Zugriff auf mehr als 10.000 Abonnements innerhalb des Abfragebereichs der Mandanten- oder Verwaltungsgruppe hat, ist die Antwort auf die ersten 10.000 Abonnements begrenzt, und der Header x-ms-tenant-subscription-limit-hit wird als true zurückgegeben.

Zur Veranschaulichung der Funktionsweise von Headern sehen wir uns eine Abfrageantwort mit dem Header und den Werten von x-ms-user-quota-remaining: 10 und x-ms-user-quota-resets-after: 00:00:03 an.

  • Innerhalb der nächsten 3 Sekunden können höchstens 10 Abfragen ohne Einschränkung übermittelt werden.
  • In 3 Sekunden werden die Werte von x-ms-user-quota-remaining bzw x-ms-user-quota-resets-after . zurückgesetzt auf 15 bzw 00:00:05 . zurückgesetzt.

Ein Beispiel für die Verwendung der Kopfzeilen zum Backoff für Abfrageanforderungen finden Sie im Beispiel in "Query" parallel.

Gruppieren von Abfragen

Das Gruppieren von Abfragen nach Abonnement, Ressourcengruppe oder einer einzelnen Ressource ist effizienter als das Parallelisieren von Abfragen. Die Kontingentkosten für eine größere Abfrage sind oft geringer als die Kontingentkosten für viele kleine und gezielte Abfragen. Die Gruppengröße sollte kleiner als 300 sein.

  • Beispiel für einen schlecht optimierten Ansatz.

    // 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);
    
    // ...
    }
    
  • Beispiel für einen optimierten Gruppierungsansatz.

    // 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);
    
      // ...
    }
    
  • Beispiel für einen optimierten Gruppierungsansatz zum Abrufen mehrerer Ressourcen in einer Abfrage.

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

Staffelungsabfragen

Aufgrund der Art und Weise, wie die Drosselung erzwungen wird, werden gestaffelte Abfragen empfohlen. Statt beispielsweise 60 Abfragen gleichzeitig zu senden, werden die Abfragen in vier 5-Sekunden-Fenster gestaffelt.

  • Nichtstaggered-Abfragezeitplan.

    Abfrageanzahl 60 0 0 0
    Zeitintervall (in Sekunden) 0-5 5-10 10-15 15 – 20
  • Gestaffelter Abfragezeitplan.

    Abfrageanzahl 15 15 15 15
    Zeitintervall (in Sekunden) 0-5 5-10 10-15 15 – 20

Der folgende Code ist ein Beispiel für die Achtung von Einschränkungsheadern beim Abfragen von 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);
    }
}

Abfrage parallel

Auch wenn die Gruppierung gegenüber der Parallelisierung empfohlen wird, gibt es Situationen, in denen Abfragen nicht einfach gruppiert werden können. In diesen Fällen können Sie Azure Resource Graph abfragen, indem Sie mehrere Abfragen parallel senden. Das folgende Beispiel zeigt, wie sie basierend auf Einschränkungsheadern backoffen werden.

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

Paginierung

Da Azure Resource Graph maximal 1.000 Einträge in einer einzelnen Abfrageantwort zurückgibt, müssen Sie Ihre Abfragen möglicherweise aus paginieren , um das gewünschte vollständige Dataset abzurufen. Einige Azure Resource Graph-Clients behandeln die Paginierung jedoch anders als andere.

Wenn Sie das „ResourceGraph“-SDK verwenden, erfolgt die Paginierung durch das Übergeben des von der vorherigen Abfrageantwort an die paginierte Abfrage zurückgegebenen Skiptokens. Das bedeutet, dass Sie Ergebnisse aus allen paginierten Aufrufen sammeln und diese am Ende vereinen müssen. In diesem Fall akzeptiert jede von Ihnen gesendete paginierte Abfrage ein Abfragekontingent.

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

Wird noch gedrosselt?

Wenn Sie die Empfehlungen dieses Artikels verwendet haben und Ihre Azure Resource Graph-Abfragen noch gedrosselt werden, wenden Sie sich an das Azure Resource Graph-Team. Das Team unterstützt Azure Resource Graph, unterstützt aber keine Microsoft Graph-Drosselung.

Geben Sie diese Details an, wenn Sie sich an das Azure Resource Graph-Team wenden:

  • Ihren spezifischen Anwendungsfall und Anforderungen an den Gerätetreiber Ihres Unternehmens für ein höheres Drosselungslimit.
  • Auf wie viele Ressourcen haben Sie Zugriff? Wie viele davon werden von einer einzelnen Abfrage zurückgegeben?
  • Welche Arten von Ressourcen möchten Sie verwenden?
  • Welches Abfragemuster verwenden Sie? X Abfragen pro Y Sekunden usw.

Nächste Schritte