【发布时间】:2017-02-20 20:06:32
【问题描述】:
我有一个 API 必须并行调用 4 个 HttpClients,支持并发 500 个用户/秒(所有用户同时调用 API)
即使不是所有 HttpClients 调用都返回了值,也必须有严格的超时时间,让 API 返回结果。
端点是外部第三方 API,我无法控制它们或知道代码。
我对此事进行了广泛的研究,但即使许多解决方案都有效,我也需要一个尽可能少消耗 CPU 的解决方案,因为我的服务器预算很低。
到目前为止,我想出了这个:
var conn0 = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(1000),
BaseAddress = new Uri("http://endpoint")
};
var conn1 = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(1000),
BaseAddress = new Uri("http://endpoint")
};
var conn2 = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(1000),
BaseAddress = new Uri("http://endpoint")
};
var conn3 = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(1000),
BaseAddress = new Uri("http://endpoint")
};
var list = new List<HttpClient>() { conn0, conn1, conn2, conn3 };
var timeout = TimeSpan.FromMilliseconds(1000);
var allTasks = new List<Task<Task>>();
//the async DoCall method just call the HttpClient endpoint and return a MyResponse object
foreach (var call in list)
{
allTasks.Add(Task.WhenAny(DoCall(call), Task.Delay(timeout)));
}
var completedTasks = await Task.WhenAll(allTasks);
var allResults = completedTasks.OfType<Task<MyResponse>>().Select(task => task.Result).ToList();
return allResults;
我使用WhenAny和两个任务,一个用于调用,一个用于超时。如果调用任务迟到了,另一个还是返回。
现在,这段代码完美运行,一切都是异步的,但我想知道是否有更好的方法来实现这一点。
对该 API 的单次调用会创建大量线程,并且有 500 个并发用户,它需要平均 8(八)台 D3_V2 Azure 4 核机器,从而导致疯狂的开支,并且超时时间越长,CPU 使用率就越高。
有没有更好的方法在不使用这么多 CPU 资源的情况下做到这一点(也许 Parallel Linq 是比这更好的选择)?
如果端点没有及时回复,HttpClient 超时是否足以停止调用并返回,而不必使用WhenAny 中的第二个任务?
更新:
- 端点是第三方 API,我不知道代码也没有任何控制权,调用以 JSON 完成并返回 JSON 或字符串。
- 他们中的一些人偶尔会在 10 多秒后回复,或者被卡住并且速度极慢,因此超时是释放线程并返回,即使来自另一个及时返回的部分数据也是如此。
- 缓存是可能的,但只是部分原因,因为数据一直在变化,例如股票和外汇实时货币交易。
【问题讨论】:
-
您能否提供有关您正在调用的端点的类型和性质的更多信息,缓存部分结果在您的用例中是否可行,为什么会出现硬超时(这似乎是成本驱动,但还有其他原因),您是否可以访问端点的代码和基础设施,或者您是否正在调用第 3 方 api?
-
我在问题中添加了一些信息。我想知道现在的实现方式是否有问题,以及由于 .net 具有多种并行/并发解决方案,是否有可能做得更好
标签: c# concurrency task-parallel-library asynchttpclient