Azure Resource Graph 中的節流要求指導方針
建立 Azure Resource Graph 資料的程式設計和頻繁使用時,應該考慮節流如何影響查詢結果。 變更要求資料的方式,可協助您和您的組織避免受到節流,並維護 Azure 資源的即時資料流量。
本文涵蓋了與在 Azure Resource Graph 中建立查詢相關的四個領域和模式:
- 了解節流標頭。
- 分組查詢。
- 錯開查詢。
- 分頁的效果。
了解節流標頭
Azure Resource Graph 會根據時間範圍,為每個使用者配置配額數字。 例如,使用者在每 5 秒的時間範圍內,最多可以傳送 15 個查詢而不會進行節流。 配額值是由許多因素所決定,而且可能會變更。
在每個查詢回應中,Azure Resource Graph 都會加入兩個節流標頭:
x-ms-user-quota-remaining
(int):使用者的剩餘資源配額。 此值會對應至查詢計數。x-ms-user-quota-resets-after
(hh:mm:ss):使用者配額耗用量重設之前的剩餘時間長度。
當安全性主體可以存取租用戶或管理群組查詢範圍內超過 10,000 個訂閱時,回應會限制為前 10,000 個訂閱,且 x-ms-tenant-subscription-limit-hit
標頭會傳回為 true
。
為了說明標頭的作用,讓我們看一下具有標頭和值 x-ms-user-quota-remaining: 10
和 x-ms-user-quota-resets-after: 00:00:03
的查詢回應。
- 在接下來的 3 秒內,最多可以提交 10 個查詢,而不會進行節流。
- 在 3 秒內,
x-ms-user-quota-remaining
和x-ms-user-quota-resets-after
的值會分別重設為15
和00:00:05
。
若要查看在查詢要求中使用標頭進行輪詢的範例,請參閱以平行方式查詢中的範例。
群組查詢
依訂用帳戶、資源群組或個別資源來分組查詢,會比平行處理查詢更有效率。 較大查詢的配額成本通常小於許多小型和目標查詢的配額成本。 建議群組大小小於 300。
最佳化方法不佳的範例。
// 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: "Resources | project name, type"); var azureOperationResponse = await this.resourceGraphClient .ResourcesWithHttpMessagesAsync(userQueryRequest, header) .ConfigureAwait(false); // ... }
最佳化群組方法的範例。
// 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); // ... }
在單一查詢中取得多個資源的最佳化群組方法範例。
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); // ... }
錯開查詢
由於強制執行節流的方式,因此建議您將查詢錯開。 例如,比起同時傳送 60 個查詢,不如將查詢錯開成四個時長 5 秒的間隔。
非交錯查詢排程。
查詢計數 60 0 0 0 時間間隔 (秒) 0-5 5-10 10-15 15-20 交錯查詢排程。
查詢計數 15 15 15 15 時間間隔 (秒) 0-5 5-10 10-15 15-20
下列程式碼是在查詢 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);
}
}
以平行方式查詢
雖然建議在平行處理時使用群組,但有時候查詢無法輕鬆地分組。 在這些情況下,您可能會想要以平行方式傳送多個查詢來查詢 Azure Resource Graph。 下列範例示範如何根據節流標頭輪詢。
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);
}
}
}
分頁
因為 Azure Resource Graph 在單一查詢回應中最多傳回 1,000 個項目,所以您可能需要將查詢分頁,以取得您想要的完整資料集。 但是,某些 Azure Resource Graph 的用戶端處理編頁的方式與其他用戶端不同。
使用 ResourceGraph SDK 時,您必須將先前查詢回應所傳回的跳過權杖傳遞至下一個編頁查詢,以處理編頁。 這種設計表示您需要從所有編頁呼叫中收集結果,並將其結合在一起。 在此情況下,您傳送的每個編頁查詢都會採用一個查詢配額。
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.
}
仍在進行節流?
如果您使用本文的建議,且 Azure Resource Graph 查詢仍在進行節流,請連絡 Azure Resource Graph 小組。 小組支援 Azure Resource Graph,但不支援 Microsoft Graph 節流。
當您連絡 Azure Resource Graph 小組時,請提供下列詳細資料:
- 您的特定使用案例和商務驅動程式需要更高的節流限制。
- 您有多少資源可以存取? 從單一查詢傳回其中多少項目?
- 您感興趣的資源類型為何?
- 您的查詢模式是什麼? 每 Y 秒 X 個查詢,依此類推。