ASP.net Web API 中的异步线程

Async threading in ASP.net Web API

我必须更新调用 AWS 中托管的第三方 API 的数千个 SKU 的价格。第三方的 TPS 限制为 1000,即每秒允许 1000 API 次调用。第三方 API 每次 API 调用大约需要 1.5 秒。

现在,如果我通过调用第三方 API 按顺序更新价格,对于 2,000 种产品,价格更新需要 2000 * 1.5 = 3000 秒。通过使用线程和线程同步,这应该在 3 秒内实现,因为 TPS 节流阀是 1000。这是我目前方法的示例代码片段:

[HttpPut]
public async Task<HttpResponseMessage> UpdatePrices([FromBody] List<PriceViewModel> prices)
{
    int failedAPICalls = 0;
    int successAPICalls = 0;
    foreach (var p in prices) {
        PriceManagement pm = new PriceManagement();
        ErrorViewModel error = await pm.UpdateMarketplacePrice(p.SKU, p.Price);
        if (error != null) {
            failedAPICalls++;
            //Error handling code here...
        }
        else {
            successAPICalls++;
            //Log price update to database
        }
    }
    var totalApiCalls = successAPICalls + failedAPICalls;
    string message = "Total API calls : " + totalApiCalls.ToString() + " | Successfull API Calls: " + successAPICalls.ToString()
        + " | Failed API Calls: " + failedAPICalls.ToString();
    return Request.CreateResponse(HttpStatusCode.OK, message);
}

这是示例定义查看模型:

public class PriceViewModel
{
    public string SKU { get; set; }
    public decimal Price { get; set; }
}

public class ErrorViewModel
{
    public int StatusCode { get; set; }
    public string Description { get; set; }
}

请帮助我提高性能。

您 post 编辑的代码是连续的。异步但仍然是顺序的。 await 将等待一个已经异步的操作在不阻塞的情况下完成,然后再继续执行。它不会同时触发所有请求。

使用特定限制进行多个并发调用的一种简单方法是使用 ActionBlock<> 并将 MaxDegreeOfParallelism 设置为您想要的限制,例如:

var options=new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = maxDegreeOfParallelism,
    BoundedCapacity = capacity
};

var block=new ActionBlock<PriceViewModel>(async p=>{
    var pm = new PriceManagement();
    var error = await pm.UpdateMarketplacePrice(p.SKU, p.Price);
    if (error != null) {
        Interlocked.Increment(ref failedAPICalls);
    }
    else {
        Interlocked.Increment(ref successAPICalls);
    }
}, options);

设置MaxDegreeOfParallelism控制可以并发处理多少消息。其余的消息被缓冲。

创建区块后,我们可以向其发送 post 消息。每条消息将由一个单独的任务处理,直至达到 MaxDOP 限制。完成后,我们会告诉块并等待它完成所有剩余的消息。

foreach(var p in prices)
{
    await block.SendAsync(p);
}
//Tell the block we're done
block.Complete();
//Wait until all prices are processed
await block.Completion;

默认情况下,可以缓冲的项目数没有限制。如果操作缓慢,这可能会成为一个问题,因为缓冲区可能最终有数千个项目等待处理,基本上复制了价格列表。

为避免这种情况,可以将 BoundedCapacity 设置为特定数字。当达到限制时 SendAsync 将阻塞,直到有可用插槽