Panduan untuk permintaan yang batasi di Azure Resource Graph

Saat membuat penggunaan data Azure Resource Graph secara terprogram dan sering, buat pertimbangan tentang bagaimana pembatasan memengaruhi hasil kueri. Mengubah cara data diminta dapat membantu Anda dan organisasi Anda menghindari pembatasan dan mempertahankan aliran data tepat waktu tentang sumber daya Azure Anda.

Artikel ini membahas empat bidang dan pola yang terkait dengan pembuatan kueri di Azure Resource Graph:

  • Memahami header pelambatan
  • Pengelompokan kueri
  • Penahapan kueri
  • Dampak paginasi

Memahami header pelambatan

Azure Resource Graph mengalokasikan nomor kuota untuk setiap pengguna berdasarkan jendela waktu. Misalnya, pengguna dapat mengirim paling banyak 15 kueri dalam setiap jangka waktu 5 detik tanpa dibatasi. Nilai kuota ditentukan oleh banyak faktor dan dapat berubah.

Dalam setiap respons kueri, Azure Resource Graph menambahkan dua header pembatasan:

  • x-ms-user-quota-remaining (int): Sisa kuota sumber daya untuk pengguna. Nilai ini dipetakan untuk penghitungan kueri.
  • x-ms-user-quota-resets-after (jj:mm:dd): Durasi waktu hingga konsumsi kuota pengguna direset.

Ketika kepala keamanan memiliki akses ke lebih dari 5.000 langganan dalam lingkup permintaanpenyewa atau grup manajemen, respons terbatas pada 5.000 langganan pertama dan header dikembalikan sebagai true .

Untuk mengilustrasikan cara kerja header, mari kita lihat respons kueri yang memiliki header dan nilai x-ms-user-quota-remaining: 10 dan x-ms-user-quota-resets-after: 00:00:03.

  • Dalam 3 detik berikutnya, paling banyak 10 kueri dapat diajukan tanpa dibatasi.
  • Dalam 3 detik, nilai x-ms-user-quota-remaining dan x-ms-user-quota-resets-after akan direset masing-masing ke 15 dan 00:00:05.

Untuk melihat contoh penggunaan header untuk melakukan backoff pada permintaan kueri, lihat sampel di Kueri secara Paralel.

Pengelompokan kueri

Pengelompokan kueri menurut langganan, grup sumber daya, atau sumber daya individual lebih efisien daripada kueri paralel. Biaya kuota kueri yang lebih besar seringkali kurang dari biaya kuota banyak kueri kecil dan yang ditargetkan. Ukuran grup disarankan kurang dari 300.

  • Contoh pendekatan yang dioptimalkan dengan buruk

    // 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);
    
    // ...
    }
    
  • Contoh #1 pendekatan pengelompokan yang dioptimalkan

    // 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);
    
      // ...
    }
    
  • Contoh #2 pengelompokan yang dioptimalkan untuk mendapatkan beberapa sumber daya dalam satu kueri

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

Penahapan kueri

Karena cara pembatasan diberlakukan, kami sarankan kueri untuk diatur. Artinya, alih-alih mengirim 60 kueri pada saat yang sama, atur kueri ke dalam empat jendela 5 detik:

  • Jadwal kueri yang tidak diatur

    Jumlah Kueri 60 0 0 0
    Interval Waktu (dtk) 0-5 5-10 +10-15 +15-20
  • Jadwal kueri yang diatur

    Jumlah Kueri 15 15 15 15
    Interval Waktu (dtk) 0-5 5-10 +10-15 +15-20

Berikut ini contoh memperhatikan header yang membatasi saat mengkueri 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);
    }
}

Kueri secara Paralel

Meskipun pengelompokan direkomendasikan melalui paralelisasi, ada kalanya kueri tidak dapat dikelompokkan dengan mudah. Dalam kasus ini, Anda mungkin ingin mengkueri Azure Resource Graph dengan mengirim beberapa kueri secara paralel. Berikut ini contoh cara melakukan backoff berdasarkan header pembatasan dalam skenario seperti itu:

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

Paginasi

Karena Azure Resource Graph menampilkan paling banyak 1.000 entri dalam satu respons kueri, Anda mungkin perlu membuat paginasi kueri untuk mendapatkan set data lengkap yang Anda cari. Namun, beberapa klien Azure Resource Graph menangani paginasi secara berbeda dari yang lain.

  • SDK C#

    Saat menggunakan ResourceGraph SDK, Anda perlu menangani paginasi dengan meneruskan token lewati yang ditampilkan dari respons kueri sebelumnya ke kueri halaman berikutnya. Desain ini berarti Anda perlu mengumpulkan hasil dari semua panggilan paginasi dan menggabungkannya bersama-sama di akhir. Dalam hal ini, setiap kueri yang dipaginasi yang Anda kirim membutuhkan satu kuota kueri:

    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.Empty(azureOperationResponse.Body.SkipToken))
    {
        queryRequest.SkipToken = azureOperationResponse.Body.SkipToken;
        // Each post call to ResourceGraph consumes one query quota
        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.
    }
    

Masih dibatasi?

Jika Anda mendapatkan pembatasan setelah menjalankan rekomendasi di atas, hubungi tim Azure Resource Graph.

Masukkan detail berikut

  • Kebutuhan penggunaan khusus dan driver bisnis Anda membutuhkan batas pembatasan yang lebih tinggi.
  • Berapa banyak sumber daya yang dapat Anda akses? Berapa banyak yang ditampilkan dari satu kueri?
  • Tipe sumber daya apa yang Anda minati?
  • Apa pola kueri Anda? Kueri X per detik Y, dan sebagainya.

Langkah berikutnya