Panduan untuk permintaan yang batasi di Azure Resource Graph

Saat membuat penggunaan data Azure Resource Graph yang terprogram dan sering, pertimbangan harus dibuat untuk 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 pembatasan.
  • Mengelompokkan kueri.
  • Kueri yang mengejutkan.
  • Efek penomoran halaman.

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 perwakilan keamanan memiliki akses ke lebih dari 10.000 langganan dalam cakupan kueri grup penyewa atau manajemen, respons dibatasi hingga 10.000 langganan pertama dan x-ms-tenant-subscription-limit-hit 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 ke depan, paling banyak 10 kueri dapat dikirimkan tanpa dibatasi.
  • Dalam 3 detik, nilai x-ms-user-quota-remaining dan x-ms-user-quota-resets-after diatur ulang ke 15 dan 00:00:05 masing-masing.

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 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 pendekatan 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. Misalnya, alih-alih mengirim 60 kueri pada saat yang sama, mengejutkan kueri ke dalam empat jendela 5 detik.

  • Jadwal kueri yang tidak diagregasi.

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

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

Kode berikut adalah contoh menghormati header pembatasan 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. Contoh berikut menunjukkan cara melakukan backoff berdasarkan header pembatasan.

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 mengembalikan maksimum 1.000 entri dalam satu respons kueri, Anda mungkin perlu mem-paginate kueri Anda untuk mendapatkan himpunan data lengkap yang Anda inginkan. Tetapi beberapa klien Azure Resource Graph menangani paginasi secara berbeda dari yang lain.

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 paginasi yang Anda kirim mengambil 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.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.
}

Masih dibatasi?

Jika Anda menggunakan rekomendasi artikel ini dan kueri Azure Resource Graph Anda masih dibatasi, hubungi tim Azure Resource Graph. Tim mendukung Azure Resource Graph tetapi tidak mendukung pembatasan Microsoft Graph.

Berikan detail ini saat Anda menghubungi tim Azure Resource Graph:

  • 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