首页 > 解决方案 > 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; }
}

请帮助我提高性能。

标签: multithreadingasp.net-web-api.net-4.5

解决方案


您发布的代码是顺序的。异步但仍然是顺序的。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控制可以同时处理的消息数量。其余消息被缓冲。

创建块后,我们可以向其发布消息。每条消息将由一个单独的任务处理,最高可达 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将阻塞,直到插槽可用


推荐阅读