gpt4 book ai didi

c# - 超时异常 - 请求排队?线程不够?

转载 作者:太空狗 更新时间:2023-10-29 20:41:46 26 4
gpt4 key购买 nike

背景:

我有一个服务,它聚合来自多个其他服务的数据。为了让事情及时发生,我在整个代码中使用 async,然后将各种请求收集到一个任务列表中。

以下是代码的一些摘录:

private async Task<List<Foo>> Baz(..., int timeout)
{
var tasks = new List<Task<IEnumerable<Foo>>>();
Tasks.Add(GetFoo1(..., timeout));
Tasks.Add(GetFoo2(..., timeout));
// Up to 6, depending on other parameters. Some tasks return multiple objects.

return await Task.WhenAll(tasks).ContinueWith((antecedent) => { return antecedent.Result.AsEnumerable().SelectMany(f => f).ToList(); }).ConfigureAwait(false);
}
private async Task<IEnumerable<Foo>> GetFoo1(..., int timeout)
{
Stopwatch sw = new Stopwatch();
sw.Start();

var value = await SomeAsyncronousService.GetAsync(..., timeout).ConfigureAwait(false);

sw.Stop();
// Record timing...
return new[] { new Foo(..., value) };
}
private async Task<IEnumerable<Foo>> GetFoo2(..., int timeout)
{
return await Task.Run(() => {
Stopwatch sw = new Stopwatch();
sw.Start();
var r = new[] { new Foo(..., SomeSyncronousService.Get(..., timeout)) };
sw.Start();
sw.Stop();
// Record timing...
return r;
}).ConfigureAwait(false);
}

// In class SomeAsyncronousService
public async Task<string> GetAsync(..., int timeout)
{
...
try
{
using (var httpClient = HttpClientFactory.Create())
{
// I have tried it with both timeout and CTS. The behavior is the same.
//httpClient.Timeout = TimeSpan.FromMilliseconds(timeout);
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

var content = ...;
var responseMessage = await httpClient.PostAsync(Endpoint, content, cts.Token).ConfigureAwait(false);
if (responseMessage.IsSuccessStatusCode)
{
var contentData = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
...
return ...
}
...
}
}
catch (OperationCanceledException ex)
{
// Log statement ...
}
catch (Exception ex)
{
// Log statement ...
}
return ...;
}

症状:

这段代码在我的本地机器上运行良好,并且大部分时间在我们的测试服务器上运行良好。然而,偶尔我们会得到一堆大量记录的超时——由上面的“记录时间”注释和关于 OperationCanceledExceptions 的日志语句记录。我无法判断我调用的服务是否真的超时了。

现在,当我说一系列超时时,我的意思是大多数或所有任务(以及除了一个使用的 HttpClient,另一个使用 WCF 服务的 HttpClient)大约在同一时间全部超时。

现在,我知道您在想什么,我正在传递相同的超时时间。没错,但我传入了 250 毫秒,并且各种秒表报告的运行时间大约为 800 毫秒或更高。

现在,我确实在日志中看到了 OperationCanceledExceptions,但异常的时间戳与秒表结束时(或在 2-3 毫秒内)的时间戳相同,并且我的服务失败,因为客户希望它做出响应在 500 毫秒或更短的时间内,而不是 800 毫秒。

现在,通常各种服务的响应时间不到 100 毫秒,结果之间存在很大差异。当我们出现问题并且大多数/全部在 800 毫秒或更长时间内返回时,它们的变化仅约 10 毫秒。我调用的依赖项都在不同的域上。他们所有人都真的需要那么长时间才能同时做出回应,这似乎是极不可能的。

我想可能存在网络问题,同时影响所有请求,但我们网络中的其他服务没有遇到相同的行为 - 它仅限于我正在编写的新服务。

即使是这种情况,我也希望取消异常会在 250 毫秒后发生,然后任务结束并且秒表记录 250(加上 5-20 毫秒左右的异常处理)。

所以我不认为这是网络问题。现在我确信至少部分问题与我没有正确取消/超时有关,但在我看来,来自服务的所有外出请求都同时受到影响,而与 HttpClient 无关。

我这么说的原因是因为当其余请求超时时,WCF 服务也显示 800+ 毫秒(根据秒表)。 WCF 服务不是异步的。超时设置如下:
var binding = new BasicHttpBinding()
{
Security = new BasicHttpSecurity()
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = new HttpTransportSecurity()
{
ClientCredentialType = HttpClientCredentialType.Ntlm
}

},
ReceiveTimeout = TimeSpan.FromMilliseconds(timeout)
};

问题:

因此,简而言之,我认为某些原因导致对任何域的所有传出请求暂停或排队,从而导致观察到的行为。

我花了几天时间试图弄清楚发生了什么,但没有运气。有任何想法吗?

编辑

我认为正在发生的事情是请求被搁置,因为没有可用的线程,然后几百毫秒后一个线程可用并且任务开始。对方法调用进行计时显示它需要 800 毫秒,但 HttpClient 上的超时不会开始,直到有线程可用于运行异步调用。

它还可以解释为什么我看到该方法需要 800+ 毫秒,但有时它仍然完成而不显示超时异常。其他时候它确实会抛出超时异常并且不会完成。

我曾尝试在 Application_Start 中将 ServicePointManager.DefaultConnectionLimit 设置为 200,但这并没有解决问题。

与我们的其他服务相比,该服务并没有占用那么多流量,而且其他服务似乎都没有出现同样的问题。

有任何想法吗?

编辑 2

我登录到该框并在进行(次要)负载测试时监视 netstat。

使用 HttpClient,每秒 1-2 个请求,端口将显示 ESTABLISHED,然后移动到 TIME_WAIT 大约 4 分钟。每秒有 3 个以上的请求,我最终会得到大约每秒 100 个恒定的请求 ESTABLISHED 端口(因此每秒 3 个负载测试为 300 个),然后我会开始看到它们转到 CLOSE_WAIT 而不是 TIME_WAIT - 指示错误条件关闭。与此同时,我会看到异常数量和执行请求的时间激增。 (TcpTimedWaitDelay 不适用于 CLOSE_WAIT)。

所以我重写了整个事情以串行使用 HttpWebRequests,而不是并行使用 HttpClient。然后我进行了相同的测试。

现在 ESTABLISHED 端口等于每秒 0-2 个请求,然后端口按预期移动到 TIME_CLOSE。性能和吞吐量有所提高,但并没有完全清除。

然后我将 TcpTimedWaitDelay 设置为 30(默认为 24​​0)。业绩大幅提升。我有一个原始负载测试,每秒 40 个请求命中它,没有任何问题。我将获得更彻底的测试设置,但我认为问题已解决。

我不知道发生了什么,但似乎 HttpClient 没有正确关闭下面的临时端口。我公司的许多开发人员和架构师都查看了它,并没有发现代码有什么问题。我尝试在每个请求的 using 语句中使用一个 HttpClient,以及在后端调用的每个 api 使用一个 HttpClient。我尝试并行和串行使用 HttpClient。我已经尝试过使用 async/await 和不使用它。无论我尝试什么,行为都是一样的。

我希望能够使用 HttpClient,但我不能再在这个问题上花时间了,因为我已经将它与 HttpWebRequest 一起使用了。我的下一步是使 HttpWebRequests 并行发生。

谢谢您的意见。

最佳答案

我在使用 HttpClient 时也遇到过类似的挫折。在我的场景中,我发现在 ServicePointManager 上将 MaxServicePointIdleTime 设置为低得多的值并将 DefaultConnectionLimit 设置为高值解决了我的问题。我相信在我的情况下,当连接保持打开时,我正在经历池饥饿。

您可能还想在发布时不附加调试器的情况下进行测试,如果您还没有这样做的话,因为 TaskScheduler 在调试时的行为会有所不同。

以下 MSDN 文章很有帮助:http://blogs.msdn.com/b/jpsanders/archive/2009/05/20/understanding-maxservicepointidletime-and-defaultconnectionlimit.aspx

关于c# - 超时异常 - 请求排队?线程不够?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24049068/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com