为了重用 HttpClient
中的开放式TCP连接,您需要为所有请求共享单个实例。
这意味着我们不能简单地使用不同的设置(例如超时或标头)来实例化 HttpClient
。
那么我们如何同时共享连接并使用不同的设置呢?事实上,旧的 HttpWebRequest
和 WebClient
基础结构默认情况下就非常容易做到这一点。
请注意,仅在进行请求之前设置 HttpClient.Timeout
不具有线程安全性,并且在并发应用程序(例如 ASP.NET 网站)中无法工作。
为了重用 HttpClient
中的开放式TCP连接,您需要为所有请求共享单个实例。
这意味着我们不能简单地使用不同的设置(例如超时或标头)来实例化 HttpClient
。
那么我们如何同时共享连接并使用不同的设置呢?事实上,旧的 HttpWebRequest
和 WebClient
基础结构默认情况下就非常容易做到这一点。
请注意,仅在进行请求之前设置 HttpClient.Timeout
不具有线程安全性,并且在并发应用程序(例如 ASP.NET 网站)中无法工作。
在幕后,HttpClient
只是使用取消令牌来实现超时行为。如果您想针对每个请求进行变化,可以直接执行相同操作:
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));
await httpClient.GetAsync("http://www.google.com", cts.Token);
请注意,HttpClient
的默认超时时间为100秒,即使您在请求级别设置了更高的值,该请求也将在那一点被取消。为解决此问题,请在 HttpClient
上设置“max”超时时间,这可以是无限大:
httpClient.Timeout = System.Threading.Timeout.InfiniteTimeSpan;
接受的答案很好,但我想为将来寻找此问题解答的任何人提供另一种场景。在我的情况下,我已经使用了CancellationTokenSource,当用户选择取消时会取消其标记。因此,在这种情况下,您仍然可以使用CancellationTokenSource的CreateLinkedTokenSource方法来使用此技术。因此,在我的情况下,http操作将通过超时或用户干预来取消。以下是一个示例:
public async static Task<HttpResponseMessage> SendRequest(CancellationToken cancellationToken)
{
var ctsForTimeout = new CancellationTokenSource();
ctsForTimeout.CancelAfter(TimeSpan.FromSeconds(5));
var cancellationTokenForTimeout = ctsForTimeout.Token;
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenForTimeout))
{
try
{
return await httpClient.GetAsync("http://asdfadsf", linkedCts.Token);
}
catch
{
//just for illustration purposes
if (cancellationTokenForTimeout.IsCancellationRequested)
{
Console.WriteLine("timeout");
}
else if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("other cancellation token cancelled");
}
throw;
}
}
}
.NET 6(及更高版本)现在包含了Task.WaitAsync(TimeSpan)
方法,您可以使用它。
await httpClient.SendAsync(request).WaitAsync(TomeSpan.FromSeconds(5));
HttpClient
的“默认超时”仍然会在默认的100秒后取消操作,即使您在WaitAsync()
中设置了5分钟。 - Fabio如果您已经在使用CancellationToken
,可能是从异步API端点中获取的,您可以使用CancellationTokenSource.CreateLinkedTokenSource
将其与另一个CancellationToken
组合。
以下是一个示例:
public async Task<HttpResponseMessage> SendRequestWithTimeout(HttpRequestMessage request, Timespan timeout, CancellationToken ct){
var timeoutCt = new CancellationTokenSource(timeout).Token;
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCt).Token;
return await httpClient.SendAsync(request, linkedCts);
}