簡體   English   中英

是否可以在 .NET 6.0 中限制 Parallel.ForEachAsync 以避免速率限制?

[英]Is it possible to throttle Parallel.ForEachAsync in .NET 6.0 to avoid rate limiting?

我對編程很陌生(< 3 年經驗),所以我對這篇文章中的主題不太了解。 請多多包涵。

我的團隊正在開發與第三方系統的集成,而第三方端點之一缺乏有意義的方式來獲取與條件匹配的實體列表。

我們一直在通過遍歷請求集合來獲取這些實體,並將每個等待調用的結果添加到列表中。 這工作得很好,但是獲取實體比從其他端點獲取實體需要更長的時間,這讓我們可以通過提供 id 列表來獲取實體列表。

.NET 6.0 引入了Parallel.ForEachAsync() ,它讓我們可以並行異步執行多個等待任務。

例如:

public async Task<List<TEntity>> GetEntitiesInParallelAsync<TEntity>(List<IRestRequest> requests) 
where TEntity : IEntity
{
    var entities = new ConcurrentBag<TEntity>();

    // Create a function that takes a RestRequest and returns the 
    // result of the request's execution, for each request
    var requestExecutionTasks = requests.Select(i => 
        new Func<Task<TEntity>>(() => GetAsync<TEntity>(i)));

    // Execute each of the functions asynchronously in parallel, 
    // and add the results to the aggregate as they come in
    await Parallel.ForEachAsync(requestExecutionTasks, new ParallelOptions
    {
        // This lets us limit the number of threads to use. -1 is unlimited
        MaxDegreeOfParallelism = -1 
    }, async (func, _) => entities.Add(await func()));

    return entities.ToList();
}

使用此代碼而不是簡單的 foreach 循環可以加快在我的測試實例上獲取約 30 個實體所需的時間,平均縮短了 91%。 棒極了。 但是,我們擔心在可能有數千個實體的客戶端系統上使用它時可能會出現速率限制。 我們有一個系統可以從他們的 API 中檢測到“您的速率受限”消息,並在重試之前提示請求一秒鍾左右,但這並不是一個好的解決方案,因為它是一種安全措施。

如果我們只是循環請求,我們可以通過在循環的每次迭代中執行類似於await Task.Delay(minimumDelay)的操作來限制調用。 如果我錯了,請糾正我,但據我了解,這在並行 foreach 執行請求時實際上不起作用,因為它會使所有請求在執行前等待相同的時間。 有沒有辦法讓每個單獨的請求在執行前等待一定的時間,只有當我們接近速率限制時? 如果可能的話,我想在不限制要使用的線程數的情況下這樣做。

我的建議是放棄Parallel.ForEachAsync方法,而使用新的Chunk LINQ 運算符與Task.WhenAll方法結合使用。 您可以像這樣每秒啟動 100 個異步操作:

public async Task<List<TEntity>> GetEntitiesInParallelAsync<TEntity>(
    List<IRestRequest> requests) where TEntity : IEntity
{
    var tasks = new List<Task<TEntity>>();
    foreach (var chunk in requests.Chunk(100))
    {
        tasks.AddRange(chunk.Select(request => GetAsync<TEntity>(request)));
        await Task.Delay(TimeSpan.FromSeconds(1.0));
    }
    return (await Task.WhenAll(tasks)).ToList();
}

假定啟動異步操作(調用GetAsync方法)所需的時間可以忽略不計。

這種方法有一個固有的缺點,即在發生異常的情況下,在所有操作完成之前不會傳播失敗。 為了比較, Parallel.ForEachAsync方法在檢測到第一個故障后停止調用異步委托並盡快完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM