在我的C#应用程序(.NET Core3.1)中,有一个自动任务,每X小时启动另一个任务,它以不同的参数多次并行运行。在这个自动任务的末尾,会调用await Task.WhenAll(tasksList).
等待并行任务的完成。
每个任务发出一个HTTPClient (使用IHttpClientFactory工厂方法)并发出一个GET请求,语法如下:
var res = await client.GetAsync(url);
if (res.IsSuccessStatusCode)
{
var exit = await res.Content.ReadAsStringAsync();
[...omitted]
}
当共享相同的GET的两个任务在最大60-70ms的距离上运行时,问题就会随机发生。有时,这两项任务都会一个接一个地失败,每个任务都有相同的例外:
System.Net.Http.HttpRequestException: Error while copying content to a stream.
---> System.IO.IOException: The response ended prematurely.
at System.Net.Http.HttpConnection.FillAsync()
at System.Net.Http.HttpConnection.ChunkedEncodingReadStream.CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionResponseContent.SerializeToStreamAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)
从日志中,我可以看到服务器如何正确启动和接收两个不同的HTTP请求。
如果删除ReadAsStringAsync部分,问题就不会发生,因此我假设它与内容读取有关(在状态代码检查之后),就好像这两个任务最终共享Get结果一样(同时发出了两个不同的活动连接)。我尝试使用ReadAsStreamAsync,但问题仍然发生(这有助于减少事件发生,尽管如此)。
另一件可能相关的事情是,检索到的结果相当重(上次我下载它时,它的结尾是一个4.5MB的.json文件,或多或少)。
应该按顺序运行每个任务吗?还是我发出的HTTP请求出错了?
如果您想测试这个问题,在这里您可以找到一个控制台应用程序的源代码,我正在使用它来重现这个问题(如果它在前20个调用之前没有发生,请重新启动该应用程序,直到它发生为止):
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<TaskRunner>();
}).UseConsoleLifetime();
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var x = services.GetRequiredService<TaskRunner>();
var result = await x.Run();
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine("Error Occured");
}
}
return 0;
}
public class TaskRunner
{
private static IHttpClientFactory _httpFactory { get; set; }
public TaskRunner(IHttpClientFactory httpFactory)
{
_httpFactory = httpFactory;
}
public async Task<string> Run()
{
Console.WriteLine("Starting loop...");
do
{
await Task.Delay(2500); // wait app loading
await SendRequest();
} while (true);
}
private static async Task SendRequest()
{
await Task.WhenAll(new Task[] { ExecuteCall(), ExecuteCall()};
}
private async static Task<bool> ExecuteCall()
{
try
{
var client = _httpFactory.CreateClient();
// fake heavy API call (> 5MB data)
var api = "https://api.npoint.io/5896085b486eed6483ce";
Console.WriteLine("Starting call at " + DateTime.Now.ToUniversalTime().ToString("o"));
var res = await client.GetAsync(api);
if (res.IsSuccessStatusCode)
{
var exit = await res.Content.ReadAsStringAsync();
/* STREAM read alternative
var ed = await res.Content.ReadAsStreamAsync();
StringBuilder result = new StringBuilder();
using var sr = new StreamReader(ed);
while (!sr.EndOfStream)
{
result.Append(await sr.ReadLineAsync());
}
var exit = result.ToString();
*/
Console.WriteLine(exit.Substring(0, 10));
//Console.WriteLine(exit);
Console.WriteLine("Ending call at " + DateTime.Now.ToUniversalTime().ToString("o"));
return true;
}
Console.WriteLine(res.StatusCode);
Console.WriteLine("Ending call at " + DateTime.Now.ToUniversalTime().ToString("o"));
return false;
}
catch (Exception ex)
{
// put breakpoint here
// Exception => called on line:78 but if content isn't read it never occurs
Console.WriteLine(ex.ToString());
return false;
}
}
}
}
}
谢谢你能给我的任何帮助/建议!
发布于 2021-05-14 14:51:38
我回答我的问题是为了离开我申请的解决方案,对于任何可能遇到相同问题的人:)
我在Api调用之前添加了以下行:
var client = _httpFactory.CreateClient();
var api = "https://api.npoint.io/5896085b486eed6483ce";
>>> client.DefaultRequestVersion = HttpVersion.Version10; // new line
var res = await client.GetAsync(api);
这个问题似乎与端点服务器有关,即在HttpVersion为11时丢弃并发连接。这可能与保持活动连接头有关,因为在10 v时,标头被设置为关闭。
https://stackoverflow.com/questions/66789634
复制相似问题