【问题标题】:How to Limit request per Second in Async Task C#如何在异步任务 C# 中限制每秒请求
【发布时间】:2019-05-15 14:00:12
【问题描述】:

我正在编写一个与 Azure Cosmos DB 交互的应用程序。我需要在一个会话中向 CosmosDB 提交 30,000 条记录。因为我使用了 .NET Core,所以我不能使用 BulkInsert dll。所以,我使用 Foreach 循环插入到 CosmosDB。但是我看到每秒请求太多,并且 CosmosDB 超出了 RU 限制。

foreach(item in listNeedInsert){
      await RequestInsertToCosmosDB(item);
}

我想在请求数达到 100 时暂停 foreach 循环。完成 100 个请求后。 foreach 将继续。

【问题讨论】:

  • await 等待已经执行的任务。它不会启动它们或控制它们的执行。
  • 如果您想将插入次数限制为每秒 N 次,请在循环中添加延迟,以确保使用 await Task.Delay(...); 每秒最多可以进行 N 次调用。
  • 您能否以编程方式知道插入是否完成?如果你这样做了,你应该做的是拿出一个计数器,然后当它达到 100 个请求时,等待插入完成然后继续
  • 你能定义一个约束吗 - 要么我只能每秒启动 n 个请求,和/或只有 n 个请求可以在给定的情况下执行时间?无论是什么约束导致您这样做,我都会对此进行编码以专门适应该约束。如果你的目标是一个像 100 这样的任意数字,如果它们仍然执行得太快会发生什么?它会是可预测的和一致的吗?如果需要限制每秒请求数,请专门执行此操作。不要做其他事情,希望它碰巧奏效。

标签: c# asynchronous task azure-cosmosdb


【解决方案1】:

您可以对列表进行分区并等待结果:

var tasks = new List<Task>();

foreach(item in listNeedInsert)
{
    var task = RequestInsertToCosmosDB(item);
    tasks.Add(task);

    if(tasks.Count == 100)
    {
        await Task.WhenAll(tasks);
        tasks.Clear();
    }
}

// Wait for anything left to finish
await Task.WhenAll(tasks);

每次运行 100 个任务时,代码都会等待它们全部完成,然后再执行最后一批。

【讨论】:

  • 100 个并发请求太多,数据库无法有效处理。我会尝试个位数范围内的东西。尝试尝试不同的数字,看看什么效果最好。如果您想要更高的效率,请使用 SemaphorSlim 及其内置的节流机制。
【解决方案2】:

你可以为每百次迭代设置一个延迟

int i = 1;
foreach(item in listNeedInsert)
{
      await RequestInsertToCosmosDB(item);
      if (i % 100 == 0)
      {
          i = 0;
          await Task.Delay(100); // Miliseconds
      }
      i++;
}

【讨论】:

  • 这也会延迟第一次迭代。
  • 感谢我修复它的提示
  • 现在在第一次延迟之后,它将在每 99 次迭代时延迟..!
【解决方案3】:

如果您真的想最大限度地提高效率并且无法进行批量更新,请考虑在这篇文章中使用 SemaphorSlim:

Throttling asynchronous tasks

一次处理 100 个并发请求的中型数据库并不是一个好主意,因为它不具备处理这种吞吐量的能力。您可以尝试使用不同的节流数字并查看最佳值,但我猜它在个位数范围内。

如果您想做一些快速而肮脏的事情,您可能会使用 Sean 的解决方案。但我会从一开始就将任务计数设置为 5,而不是 100。

【讨论】:

    【解决方案4】:

    https://github.com/thomhurst/EnumerableAsyncProcessor

    我编写了一个库来帮助处理这种逻辑。

    用法如下:

    await AsyncProcessorBuilder.WithItems(listNeedInsert) // Or Extension Method: listNeedInsert.ToAsyncProcessorBuilder()
            .ForEachAsync(item => RequestInsertToCosmosDB(item), CancellationToken.None)
            .ProcessInBatches(batchSize: 100);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-18
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多