如何在 C# 中添加超时并重试 url 调用?
How to add timeout and retry to url call in C#?
我有一个 .tgz
文件需要下载,给定 Testing
文件夹中的 url。我可以使用 WebClient
.
从 url 成功下载 .tgz
文件
下面是我的代码:
private void DownloadTGZFile(string url, string fileToDownload)
{
using (var client = new WebClient())
{
client.DownloadFile(url + fileToDownload, "Testing/configs.tgz");
}
}
我想看看如何为这个调用添加超时,这样如果 url 在特定时间内没有响应,那么它应该超时但它可以重试 3 次然后给向上。我还想看看如何在这里使用 HttpClient
而不是 WebClient
考虑到它是较旧的 BCL class 并且不推荐。
要使用 HttpClient
下载文件,您可以执行以下操作:
// Is better to not initialize a new HttpClient each time you make a request,
// it could cause socket exhaustion
private static HttpClient _httpClient = new HttpClient()
{
Timeout = TimeSpan.FromSeconds(5)
};
public async Task<byte[]> GetFile(string fileUrl)
{
using (var httpResponse = await _httpClient.GetAsync(fileUrl))
{
// Throws an exception if response status code isn't 200
httpResponse.EnsureSuccessStatusCode();
return await httpResponse.Content.ReadAsByteArrayAsync();
}
}
For more details about socket exhaustion with HttpClient
如您所见,要为 Http
调用定义超时,您应该在创建新的 HttpClient
.
时设置超时
要为之前的代码实施重试策略,我会安装 Polly NuGet package 然后:
public async Task<byte[]> GetFile(string fileUrl)
{
return await Policy
.Handle<TaskCanceledException>() // The exception thrown by HttpClient when goes in timeout
.WaitAndRetryAsync(retryCount: 3, sleepDurationProvider: i => TimeSpan.FromMilliseconds(300))
.ExecuteAsync(async () =>
{
using (var httpResponse = await _httpClient.GetAsync(fileUrl))
{
// Throws an exception if response status code isn't 200
httpResponse.EnsureSuccessStatusCode();
return await httpResponse.Content.ReadAsByteArrayAsync();
}
});
}
在这种情况下,我定义了 3 次重试,每次尝试之间的间隔为 300 毫秒。另请注意,我没有为每种 Exception
定义重试,因为如果 - 例如 - 你输入了一个无效的 URL
,重试是无意义的。
最后,如果你想将该字节数组保存到文件中,你可以这样做:
File.WriteAllBytes(@"MyPath\file.extension", byteArray);
您可以在不依赖于外部库的情况下使用此函数。它适用于任何文件大小。
EDIT 传播 TaskCanceledException
.
的版本
public async Task<bool> DownloadFileAsync(string url,
string destinationFile,
TimeSpan timeout,
int maxTries = 3,
CancellationToken token = default)
{
using (var client = new HttpClient { Timeout = timeout })
{
for (var i = 0; i < maxTries; i++, token.ThrowIfCancellationRequested())
{
try
{
var response = await client.GetAsync(url, token);
if (!response.IsSuccessStatusCode)
continue;
var responseStream = await response.Content.ReadAsStreamAsync();
using (var outputStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
await responseStream.CopyToAsync(outputStream, 8 * 1024, token);
return true;
}
}
catch (HttpRequestException)
{
//ignore
}
}
return false;
}
}
我有一个 .tgz
文件需要下载,给定 Testing
文件夹中的 url。我可以使用 WebClient
.
.tgz
文件
下面是我的代码:
private void DownloadTGZFile(string url, string fileToDownload)
{
using (var client = new WebClient())
{
client.DownloadFile(url + fileToDownload, "Testing/configs.tgz");
}
}
我想看看如何为这个调用添加超时,这样如果 url 在特定时间内没有响应,那么它应该超时但它可以重试 3 次然后给向上。我还想看看如何在这里使用 HttpClient
而不是 WebClient
考虑到它是较旧的 BCL class 并且不推荐。
要使用 HttpClient
下载文件,您可以执行以下操作:
// Is better to not initialize a new HttpClient each time you make a request,
// it could cause socket exhaustion
private static HttpClient _httpClient = new HttpClient()
{
Timeout = TimeSpan.FromSeconds(5)
};
public async Task<byte[]> GetFile(string fileUrl)
{
using (var httpResponse = await _httpClient.GetAsync(fileUrl))
{
// Throws an exception if response status code isn't 200
httpResponse.EnsureSuccessStatusCode();
return await httpResponse.Content.ReadAsByteArrayAsync();
}
}
For more details about socket exhaustion with HttpClient
如您所见,要为 Http
调用定义超时,您应该在创建新的 HttpClient
.
要为之前的代码实施重试策略,我会安装 Polly NuGet package 然后:
public async Task<byte[]> GetFile(string fileUrl)
{
return await Policy
.Handle<TaskCanceledException>() // The exception thrown by HttpClient when goes in timeout
.WaitAndRetryAsync(retryCount: 3, sleepDurationProvider: i => TimeSpan.FromMilliseconds(300))
.ExecuteAsync(async () =>
{
using (var httpResponse = await _httpClient.GetAsync(fileUrl))
{
// Throws an exception if response status code isn't 200
httpResponse.EnsureSuccessStatusCode();
return await httpResponse.Content.ReadAsByteArrayAsync();
}
});
}
在这种情况下,我定义了 3 次重试,每次尝试之间的间隔为 300 毫秒。另请注意,我没有为每种 Exception
定义重试,因为如果 - 例如 - 你输入了一个无效的 URL
,重试是无意义的。
最后,如果你想将该字节数组保存到文件中,你可以这样做:
File.WriteAllBytes(@"MyPath\file.extension", byteArray);
您可以在不依赖于外部库的情况下使用此函数。它适用于任何文件大小。
EDIT 传播 TaskCanceledException
.
public async Task<bool> DownloadFileAsync(string url,
string destinationFile,
TimeSpan timeout,
int maxTries = 3,
CancellationToken token = default)
{
using (var client = new HttpClient { Timeout = timeout })
{
for (var i = 0; i < maxTries; i++, token.ThrowIfCancellationRequested())
{
try
{
var response = await client.GetAsync(url, token);
if (!response.IsSuccessStatusCode)
continue;
var responseStream = await response.Content.ReadAsStreamAsync();
using (var outputStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
await responseStream.CopyToAsync(outputStream, 8 * 1024, token);
return true;
}
}
catch (HttpRequestException)
{
//ignore
}
}
return false;
}
}